Merge remote-tracking branch 'neatchee/feat/emoji_reactions'
This commit is contained in:
commit
67687000be
|
@ -15,6 +15,12 @@
|
||||||
"webben.browserslist"
|
"webben.browserslist"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/sshd:1": {
|
||||||
|
"version": "latest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
// This can be used to network with other containers or the host.
|
// This can be used to network with other containers or the host.
|
||||||
"forwardPorts": [3000, 4000],
|
"forwardPorts": [3000, 4000],
|
||||||
|
|
16
.eslintrc.js
16
.eslintrc.js
|
@ -1,6 +1,10 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
],
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
node: true,
|
node: true,
|
||||||
|
@ -64,8 +68,8 @@ module.exports = {
|
||||||
eqeqeq: 'error',
|
eqeqeq: 'error',
|
||||||
indent: ['warn', 2],
|
indent: ['warn', 2],
|
||||||
'jsx-quotes': ['error', 'prefer-single'],
|
'jsx-quotes': ['error', 'prefer-single'],
|
||||||
|
'no-case-declarations': 'off',
|
||||||
'no-catch-shadow': 'error',
|
'no-catch-shadow': 'error',
|
||||||
'no-cond-assign': 'error',
|
|
||||||
'no-console': [
|
'no-console': [
|
||||||
'warn',
|
'warn',
|
||||||
{
|
{
|
||||||
|
@ -75,18 +79,16 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'no-fallthrough': 'error',
|
'no-empty': 'off',
|
||||||
'no-irregular-whitespace': 'error',
|
|
||||||
'no-mixed-spaces-and-tabs': 'warn',
|
|
||||||
'no-nested-ternary': 'warn',
|
'no-nested-ternary': 'warn',
|
||||||
|
'no-prototype-builtins': 'off',
|
||||||
'no-restricted-properties': [
|
'no-restricted-properties': [
|
||||||
'error',
|
'error',
|
||||||
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
||||||
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
||||||
],
|
],
|
||||||
|
'no-self-assign': 'off',
|
||||||
'no-trailing-spaces': 'warn',
|
'no-trailing-spaces': 'warn',
|
||||||
'no-undef': 'error',
|
|
||||||
'no-unreachable': 'error',
|
|
||||||
'no-unused-expressions': 'error',
|
'no-unused-expressions': 'error',
|
||||||
'no-unused-vars': [
|
'no-unused-vars': [
|
||||||
'error',
|
'error',
|
||||||
|
@ -96,6 +98,7 @@ module.exports = {
|
||||||
ignoreRestSiblings: true,
|
ignoreRestSiblings: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'no-useless-escape': 'off',
|
||||||
'object-curly-spacing': ['error', 'always'],
|
'object-curly-spacing': ['error', 'always'],
|
||||||
'padded-blocks': [
|
'padded-blocks': [
|
||||||
'error',
|
'error',
|
||||||
|
@ -105,7 +108,6 @@ module.exports = {
|
||||||
],
|
],
|
||||||
quotes: ['error', 'single'],
|
quotes: ['error', 'single'],
|
||||||
semi: 'error',
|
semi: 'error',
|
||||||
strict: 'off',
|
|
||||||
'valid-typeof': 'error',
|
'valid-typeof': 'error',
|
||||||
|
|
||||||
'react/jsx-boolean-value': 'error',
|
'react/jsx-boolean-value': 'error',
|
||||||
|
|
63
.github/workflows/codeql.yml
vendored
63
.github/workflows/codeql.yml
vendored
|
@ -1,11 +1,11 @@
|
||||||
name: "CodeQL"
|
name: 'CodeQL'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: ['main']
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ "main" ]
|
branches: ['main']
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '22 6 * * 1'
|
- cron: '22 6 * * 1'
|
||||||
|
|
||||||
|
@ -21,43 +21,42 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: [ 'javascript', 'ruby' ]
|
language: ['javascript', 'ruby']
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
# By default, queries listed here will override any specified in a config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
# queries: security-extended,security-and-quality
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v2
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
# - run: |
|
||||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
# echo "Run, Build Application using script"
|
||||||
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
# - run: |
|
- name: Perform CodeQL Analysis
|
||||||
# echo "Run, Build Application using script"
|
uses: github/codeql-action/analyze@v2
|
||||||
# ./location_of_script_within_repo/buildscript.sh
|
with:
|
||||||
|
category: '/language:${{matrix.language}}'
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
||||||
|
|
38
.github/workflows/lint-json.yml
vendored
Normal file
38
.github/workflows/lint-json.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
name: JSON Linting
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'dependabot/**'
|
||||||
|
paths:
|
||||||
|
- 'package.json'
|
||||||
|
- 'yarn.lock'
|
||||||
|
- '.prettier*'
|
||||||
|
- '**/*.json'
|
||||||
|
- '.github/workflows/lint-json.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'package.json'
|
||||||
|
- 'yarn.lock'
|
||||||
|
- '.prettier*'
|
||||||
|
- '**/*.json'
|
||||||
|
- '.github/workflows/lint-json.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Install all yarn packages
|
||||||
|
run: yarn --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Prettier
|
||||||
|
run: yarn prettier --check "**/*.json"
|
40
.github/workflows/lint-yml.yml
vendored
Normal file
40
.github/workflows/lint-yml.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
name: YML Linting
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'dependabot/**'
|
||||||
|
paths:
|
||||||
|
- 'package.json'
|
||||||
|
- 'yarn.lock'
|
||||||
|
- '.prettier*'
|
||||||
|
- '**/*.yaml'
|
||||||
|
- '**/*.yml'
|
||||||
|
- '.github/workflows/lint-yml.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'package.json'
|
||||||
|
- 'yarn.lock'
|
||||||
|
- '.prettier*'
|
||||||
|
- '**/*.yaml'
|
||||||
|
- '**/*.yml'
|
||||||
|
- '.github/workflows/lint-yml.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Install all yarn packages
|
||||||
|
run: yarn --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Prettier
|
||||||
|
run: yarn prettier --check "**/*.{yml,yaml}"
|
2
.github/workflows/linter.yml
vendored
2
.github/workflows/linter.yml
vendored
|
@ -57,8 +57,6 @@ jobs:
|
||||||
cache: yarn
|
cache: yarn
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
- name: Check prettier formatting
|
|
||||||
run: yarn format-check
|
|
||||||
- name: Set-up RuboCop Problem Mathcher
|
- name: Set-up RuboCop Problem Mathcher
|
||||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
uses: r7kamura/rubocop-problem-matchers-action@v1
|
||||||
- name: Set-up Stylelint Problem Matcher
|
- name: Set-up Stylelint Problem Matcher
|
||||||
|
|
|
@ -70,3 +70,10 @@ docker-compose.override.yml
|
||||||
# Ignore locale files
|
# Ignore locale files
|
||||||
/app/javascript/mastodon/locales
|
/app/javascript/mastodon/locales
|
||||||
/config/locales
|
/config/locales
|
||||||
|
|
||||||
|
# Ignore glitch-soc locale files
|
||||||
|
/app/javascript/flavours/glitch/locales
|
||||||
|
/config/locales-glitch
|
||||||
|
|
||||||
|
# Ignore glitch-soc emoji map file
|
||||||
|
/app/javascript/flavours/glitch/features/emoji/emoji_map.json
|
||||||
|
|
|
@ -250,7 +250,7 @@ Metrics/ModuleLength:
|
||||||
|
|
||||||
Metrics/ParameterLists:
|
Metrics/ParameterLists:
|
||||||
Max: 5 # RuboCop default 5
|
Max: 5 # RuboCop default 5
|
||||||
CountKeywordArgs: true # RuboCop default true
|
CountKeywordArgs: true # RuboCop default true
|
||||||
MaxOptionalParameters: 3 # RuboCop default 3
|
MaxOptionalParameters: 3 # RuboCop default 3
|
||||||
Exclude:
|
Exclude:
|
||||||
- app/models/concerns/account_interactions.rb
|
- app/models/concerns/account_interactions.rb
|
||||||
|
|
176
CHANGELOG.md
176
CHANGELOG.md
|
@ -3,6 +3,182 @@ Changelog
|
||||||
|
|
||||||
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.0] - UNRELEASED
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Add support for importing/exporting server-wide domain blocks** ([enbylenore](https://github.com/mastodon/mastodon/pull/20597), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21471), [dariusk](https://github.com/mastodon/mastodon/pull/22803), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21470))
|
||||||
|
- Add listing of followed hashtags ([connorshea](https://github.com/mastodon/mastodon/pull/21773))
|
||||||
|
- Add support for editing media description and focus point of already-sent posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20878))
|
||||||
|
- Add follow request banner on account header ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20785))
|
||||||
|
- Add confirmation screen when handling reports ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22375), [Gargron](https://github.com/mastodon/mastodon/pull/23156))
|
||||||
|
- Add option to make the landing page be `/about` even when trends are enabled ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20808))
|
||||||
|
- Add `noindex` setting back to the admin interface ([prplecake](https://github.com/mastodon/mastodon/pull/22205))
|
||||||
|
- Add instance peers API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22810))
|
||||||
|
- Add instance activity API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22833))
|
||||||
|
- Add `account.approved` webhook ([Saiv46](https://github.com/mastodon/mastodon/pull/22938))
|
||||||
|
- Add 12 hours option to polls ([Pleclown](https://github.com/mastodon/mastodon/pull/21131))
|
||||||
|
- Add dropdown menu item to open admin interface for remote domains ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21895))
|
||||||
|
- Add `--remove-headers`, `--prune-profiles` and `--include-follows` flags to `tootctl media remove` ([evanphilip](https://github.com/mastodon/mastodon/pull/22149))
|
||||||
|
- Add `--email` and `--dry-run` options to `tootctl accounts delete` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22328))
|
||||||
|
- Add `tootctl accounts migrate` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22330))
|
||||||
|
- Add `tootctl accounts prune` ([tribela](https://github.com/mastodon/mastodon/pull/18397))
|
||||||
|
- Add `tootctl domains purge` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22063))
|
||||||
|
- Add `SIDEKIQ_CONCURRENCY` environment variable ([muffinista](https://github.com/mastodon/mastodon/pull/19589))
|
||||||
|
- Add `MIN_THREADS` environment variable to set minimum Puma threads ([jimeh](https://github.com/mastodon/mastodon/pull/21048))
|
||||||
|
- Add explanation text to log-in page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20946))
|
||||||
|
- Add user profile OpenGraph tag on post pages ([bramus](https://github.com/mastodon/mastodon/pull/21423))
|
||||||
|
- Add maskable icon support for Android ([workeffortwaste](https://github.com/mastodon/mastodon/pull/20904))
|
||||||
|
- Add Belarusian to supported languages ([Mixaill](https://github.com/mastodon/mastodon/pull/22022))
|
||||||
|
- Add Western Frisian to supported languages ([ykzts](https://github.com/mastodon/mastodon/pull/18602))
|
||||||
|
- Add Montenegrin to the language picker ([ayefries](https://github.com/mastodon/mastodon/pull/21013))
|
||||||
|
- Add Southern Sami and Lule Sami to the language picker ([Jullan-M](https://github.com/mastodon/mastodon/pull/21262))
|
||||||
|
- Add logging for Rails cache timeouts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21667))
|
||||||
|
- Add color highlight for active hashtag “follow” button ([MFTabriz](https://github.com/mastodon/mastodon/pull/21629))
|
||||||
|
- Add brotli compression to `assets:precompile` ([Izorkin](https://github.com/mastodon/mastodon/pull/19025))
|
||||||
|
- Add “disabled” account filter to the `/admin/accounts` UI ([tribela](https://github.com/mastodon/mastodon/pull/21282))
|
||||||
|
- Add transparency to modal background for accessibility ([edent](https://github.com/mastodon/mastodon/pull/18081))
|
||||||
|
- Add `title` attribute to video elements in media attachments ([bramus](https://github.com/mastodon/mastodon/pull/21420))
|
||||||
|
- Add left and right margins to emojis ([dsblank](https://github.com/mastodon/mastodon/pull/20464))
|
||||||
|
- Add `reading:autoplay:gifs` to `/api/v1/preferences` ([j-f1](https://github.com/mastodon/mastodon/pull/22706))
|
||||||
|
- Add `hide_collections` parameter to `/api/v1/accounts/credentials` ([CarlSchwan](https://github.com/mastodon/mastodon/pull/22790))
|
||||||
|
- Add more specific error messages to HTTP signature verification ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21617))
|
||||||
|
- Add Storj DCS to cloud object storage options in the `mastodon:setup` rake task ([jtolio](https://github.com/mastodon/mastodon/pull/21929))
|
||||||
|
- Add checkmark symbol in the checkbox for sensitive media ([sidp](https://github.com/mastodon/mastodon/pull/22795))
|
||||||
|
- Add missing accessibility attributes to logout link in modals ([kytta](https://github.com/mastodon/mastodon/pull/22549))
|
||||||
|
- Add missing accessibility attributes to “Hide image” button in `MediaGallery` ([hs4man21](https://github.com/mastodon/mastodon/pull/22513))
|
||||||
|
- Add missing accessibility attributes to hide content warning field when disabled ([hs4man21](https://github.com/mastodon/mastodon/pull/22568))
|
||||||
|
- Add `aria-hidden` to footer circle dividers to improve accessibility ([hs4man21](https://github.com/mastodon/mastodon/pull/22576))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Ensure exact match is the first result in hashtag searches** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21315))
|
||||||
|
- Change account search to return followed accounts first ([dariusk](https://github.com/mastodon/mastodon/pull/22956))
|
||||||
|
- Change batch account suspension to create a strike ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20897))
|
||||||
|
- Change default reply language to match the default language when replying to a translated post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22272))
|
||||||
|
- Change misleading wording about waitlists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20850))
|
||||||
|
- Increase width of the unread notification border ([connorshea](https://github.com/mastodon/mastodon/pull/21692))
|
||||||
|
- Change new post notification button on profiles to make it more apparent when it is enabled ([tribela](https://github.com/mastodon/mastodon/pull/22541))
|
||||||
|
- Change trending tags admin interface to always show batch action controls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23013))
|
||||||
|
- Change wording of some OAuth scope descriptions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22491))
|
||||||
|
- Change wording of admin report handling actions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18388))
|
||||||
|
- Change confirm prompts for relationships management ([tribela](https://github.com/mastodon/mastodon/pull/19411))
|
||||||
|
- Change language surrounding disability in prompts for media descriptions ([hs4man21](https://github.com/mastodon/mastodon/pull/20923))
|
||||||
|
- Change confusing wording in the sign in banner ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22490))
|
||||||
|
- Change account moderation notes to make links clickable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22553))
|
||||||
|
- Save avatar or header correctly even if the other one fails ([tribela](https://github.com/mastodon/mastodon/pull/18465))
|
||||||
|
- Change `referrer-policy` to `same-origin` application-wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23014), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23037))
|
||||||
|
- Add 'private' to `Cache-Control`, match Rails expectations ([daxtens](https://github.com/mastodon/mastodon/pull/20608))
|
||||||
|
- Make the button that expands the compose form differentiable from the button that publishes a post ([Tak](https://github.com/mastodon/mastodon/pull/20864))
|
||||||
|
- Change automatic post deletion configuration to be accessible to moved users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20774))
|
||||||
|
- Make tag following idempotent ([trwnh](https://github.com/mastodon/mastodon/pull/20860), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21285))
|
||||||
|
- Use buildx functions for faster builds ([inductor](https://github.com/mastodon/mastodon/pull/20692))
|
||||||
|
- Split off Dockerfile components for faster builds ([moritzheiber](https://github.com/mastodon/mastodon/pull/20933), [ineffyble](https://github.com/mastodon/mastodon/pull/20948), [BtbN](https://github.com/mastodon/mastodon/pull/21028))
|
||||||
|
- Change last occurrence of “silence” to “limit” in UI text ([cincodenada](https://github.com/mastodon/mastodon/pull/20637))
|
||||||
|
- Change “hide toot” to “hide post” ([seanthegeek](https://github.com/mastodon/mastodon/pull/22385))
|
||||||
|
- Don't allow URLs that contain non-normalized paths to be verified ([dgl](https://github.com/mastodon/mastodon/pull/20999))
|
||||||
|
- Change the “Trending now” header to be a link to the Explore page ([connorshea](https://github.com/mastodon/mastodon/pull/21759))
|
||||||
|
- Change PostgreSQL connection timeout from 2 minutes to 15 seconds ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21790))
|
||||||
|
- Make handle more easily selectable on profile page ([cadars](https://github.com/mastodon/mastodon/pull/21479))
|
||||||
|
- Allow admins to refresh remotely-suspended accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22327))
|
||||||
|
- Change dropdown menu to contain “Copy link to post” even for non-public posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21316))
|
||||||
|
- Allow adding relays in secure mode and limited federation mode ([ineffyble](https://github.com/mastodon/mastodon/pull/22324))
|
||||||
|
- Change timestamps to be displayed using the user's timezone throughout the moderation interface ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/21878), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22555))
|
||||||
|
- Change CSP directives on API to be tight and concise ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20960))
|
||||||
|
- Change web UI to not autofocus the compose form ([raboof](https://github.com/mastodon/mastodon/pull/16517))
|
||||||
|
- Change idempotency key handling for posting when database access is slow ([lambda](https://github.com/mastodon/mastodon/pull/21840))
|
||||||
|
- Change remote media files to be downloaded outside of transactions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21796))
|
||||||
|
- Improve contrast of charts in “poll has ended” notifications ([j-f1](https://github.com/mastodon/mastodon/pull/22575))
|
||||||
|
- Change OEmbed detection and validation to be somewhat more lenient ([ineffyble](https://github.com/mastodon/mastodon/pull/22533))
|
||||||
|
- Widen ElasticSearch version detection to not display a warning for OpenSearch ([VyrCossont](https://github.com/mastodon/mastodon/pull/22422), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23064))
|
||||||
|
- Change link verification to allow pages larger than 1MB as long as the link is in the first 1MB ([untitaker](https://github.com/mastodon/mastodon/pull/22879))
|
||||||
|
- Update default Node.js version to Node.js 16 ([ineffyble](https://github.com/mastodon/mastodon/pull/22223), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22342))
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Officially remove support for Ruby 2.6 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21477))
|
||||||
|
- Remove LDSignature on actor Delete activities ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21466))
|
||||||
|
- Remove `object-fit` polyfill used for old versions of Microsoft Edge ([shuuji3](https://github.com/mastodon/mastodon/pull/22693))
|
||||||
|
- Remove empty `title` tag from mailer layout ([nametoolong](https://github.com/mastodon/mastodon/pull/23078))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Fix changing domain block severity not undoing individual account effects** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22135))
|
||||||
|
- Fix suspension worker crashing on S3-compatible setups without ACL support ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22487))
|
||||||
|
- Fix possible race conditions when suspending/unsuspending accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22363))
|
||||||
|
- Fix being stuck in edit mode when deleting the edited status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22126))
|
||||||
|
- Fix some performance issues with `/admin/instances` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21907))
|
||||||
|
- Fix some pre-4.0 admin audit logs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22091))
|
||||||
|
- Fix missing OAuth scopes for admin APIs ([trwnh](https://github.com/mastodon/mastodon/pull/20918), [trwnh](https://github.com/mastodon/mastodon/pull/20979))
|
||||||
|
- Fix voter count not being cleared when a poll is reset ([afontenot](https://github.com/mastodon/mastodon/pull/21700))
|
||||||
|
- Fix attachments of edited statuses not being fetched ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21565))
|
||||||
|
- Fix irreversible and whole_word parameters handling in `/api/v1/filters` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21988))
|
||||||
|
- Fix 500 error when marking posts as sensitive while some of them are deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22134))
|
||||||
|
- Fix expanded statuses not always being scrolled into view ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21797))
|
||||||
|
- Fix not being able to scroll the remote interaction modal on small screens ([xendke](https://github.com/mastodon/mastodon/pull/21763))
|
||||||
|
- Fix disappearing “Explore” tabs on Safari ([nyura](https://github.com/mastodon/mastodon/pull/20917), [ykzts](https://github.com/mastodon/mastodon/pull/20982))
|
||||||
|
- Fix wrong padding in RTL layout ([Gargron](https://github.com/mastodon/mastodon/pull/23157))
|
||||||
|
- Fix being unable to get a single EmailDomainBlock from the admin API ([trwnh](https://github.com/mastodon/mastodon/pull/20846))
|
||||||
|
- Fix pagination of followed tags ([trwnh](https://github.com/mastodon/mastodon/pull/20861))
|
||||||
|
- Fix dropdown menu positions when scrolling ([sidp](https://github.com/mastodon/mastodon/pull/22916), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23062))
|
||||||
|
- Fix mysterious registration failure when “Require a reason to join” is set with open registrations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22127))
|
||||||
|
- Fix attachment rendering of edited posts in OpenGraph ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22270))
|
||||||
|
- Fix invalid/empty RSS feed link on account pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20772))
|
||||||
|
- Fix error in `VerifyLinkService` when processing links with no href ([joshuap](https://github.com/mastodon/mastodon/pull/20741))
|
||||||
|
- Fix media uploads with FFmpeg 5 ([dead10ck](https://github.com/mastodon/mastodon/pull/21191))
|
||||||
|
- Fix sensitive flag not being set when replying to a post with a content warning under certain conditions ([kedamaDQ](https://github.com/mastodon/mastodon/pull/21724))
|
||||||
|
- Fix “Share @user's profile” profile menu item not working ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21490))
|
||||||
|
- Fix crash and incorrect behavior in `tootctl domains crawl` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19004))
|
||||||
|
- Fix autoplay on iOS ([jamesadney](https://github.com/mastodon/mastodon/pull/21422))
|
||||||
|
- Fix spaces not being stripped in admin account search ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21324))
|
||||||
|
- Fix spaces not being stripped when adding relays ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22655))
|
||||||
|
- Fix infinite loading spinner instead of soft 404 for non-existing remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21303))
|
||||||
|
- Fix minor visual issue with the top border of verified account fields ([j-f1](https://github.com/mastodon/mastodon/pull/22006))
|
||||||
|
- Fix pending account approval and rejection not being recorded in the admin audit log ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/22088))
|
||||||
|
- Fix “Sign up” button with closed registrations not opening modal on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22060))
|
||||||
|
- Fix UI header overflowing on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21783))
|
||||||
|
- Fix 500 error when trying to migrate to an invalid address ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21462))
|
||||||
|
- Fix crash when trying to fetch unobtainable avatar of user using external authentication ([lochiiconnectivity](https://github.com/mastodon/mastodon/pull/22462))
|
||||||
|
- Fix potential duplicate statuses in Explore tab ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22121))
|
||||||
|
- Fix deprecation warning in `tootctl accounts rotate` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22120))
|
||||||
|
- Fix missing style in warning and strike cards ([AtelierSnek](https://github.com/mastodon/mastodon/pull/22177), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22302))
|
||||||
|
- Fix wasteful request to `/api/v1/custom_emojis` when not logged in ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22326))
|
||||||
|
- Fix replies sometimes being delivered to user-blocked domains ([tribela](https://github.com/mastodon/mastodon/pull/22117))
|
||||||
|
- Fix admin dashboard crash when using some ElasticSearch replacements ([cortices](https://github.com/mastodon/mastodon/pull/21006))
|
||||||
|
- Fix profile avatar being slightly offset into left border ([RiedleroD](https://github.com/mastodon/mastodon/pull/20994))
|
||||||
|
- Fix N+1 queries in `NotificationsController` ([nametoolong](https://github.com/mastodon/mastodon/pull/21202))
|
||||||
|
- Fix being unable to react to announcements with the keycap number sign emoji ([kescherCode](https://github.com/mastodon/mastodon/pull/22231))
|
||||||
|
- Fix height computation of post embeds ([hodgesmr](https://github.com/mastodon/mastodon/pull/22141))
|
||||||
|
- Fix accessibility issue of the search bar due to hidden placeholder ([alexstine](https://github.com/mastodon/mastodon/pull/21275))
|
||||||
|
- Fix layout change handler not being removed due to a typo ([nschonni](https://github.com/mastodon/mastodon/pull/21829))
|
||||||
|
- Fix typo in the default `S3_HOSTNAME` used in the `mastodon:setup` rake task ([danp](https://github.com/mastodon/mastodon/pull/19932))
|
||||||
|
- Fix the top action bar appearing in the multi-column layout ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20943))
|
||||||
|
- Fix inability to use local LibreTranslate without setting `ALLOWED_PRIVATE_ADDRESSES` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21926))
|
||||||
|
- Fix punycoded local domains not being prettified in initial state ([Tritlo](https://github.com/mastodon/mastodon/pull/21440))
|
||||||
|
- Fix CSP violation warning by removing inline CSS from SVG logo ([luxiaba](https://github.com/mastodon/mastodon/pull/20814))
|
||||||
|
- Fix margin for search field on medium window size ([minacle](https://github.com/mastodon/mastodon/pull/21606))
|
||||||
|
- Fix search popout scrolling with the page in single-column mode ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/16463))
|
||||||
|
- Fix minor status cache hydration discrepancy ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19879))
|
||||||
|
- Fix `・` detection in hashtags ([parthoghosh24](https://github.com/mastodon/mastodon/pull/22888))
|
||||||
|
- Fix hashtag follows bypassing user blocks ([tribela](https://github.com/mastodon/mastodon/pull/22849))
|
||||||
|
- Fix moved accounts being incorrectly redirected to account settings when trying to view a remote profile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22497))
|
||||||
|
- Fix site upload validations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22479))
|
||||||
|
- Fix “Add new domain block” button using last submitted search value instead of the current one ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22485))
|
||||||
|
- Fix misleading hashtag warning when posting with “Followers only” or “Mentioned people only” visibility ([n0toose](https://github.com/mastodon/mastodon/pull/22827))
|
||||||
|
- Fix embedded posts with videos grabbing focus ([Akkiesoft](https://github.com/mastodon/mastodon/pull/22778))
|
||||||
|
- Fix `$` not being escaped in `.env.production` files generated by the `mastodon:setup` rake task ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23012), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23072))
|
||||||
|
- Fix sanitizer parsing link text as HTML when stripping unsupported links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22558))
|
||||||
|
- Fix `scheduled_at` input not using `datetime-local` when editing announcements ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21896))
|
||||||
|
- Fix REST API serializer for `Account` not including `moved` when the moved account has itself moved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22483))
|
||||||
|
- Fix `/api/v1/admin/trends/tags` using wrong serializer ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18943))
|
||||||
|
- Fix situations in which instance actor can be set to a Mastodon-incompatible name ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22307))
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Add `form-action` CSP directive ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20781), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20958), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20962))
|
||||||
|
- Fix unbounded recursion in account discovery ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22025))
|
||||||
|
- Revoke all authorized applications on password reset ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/21325))
|
||||||
|
|
||||||
## [4.0.2] - 2022-11-15
|
## [4.0.2] - 2022-11-15
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
[homepage]: https://contributor-covenant.org
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
[version]: https://contributor-covenant.org/version/1/4/
|
||||||
|
|
|
@ -6,6 +6,12 @@ Here are some guidelines, and ways you can help.
|
||||||
> (This document is a bit of a work-in-progress, so please bear with us.
|
> (This document is a bit of a work-in-progress, so please bear with us.
|
||||||
> If you don't see what you're looking for here, please don't hesitate to reach out!)
|
> If you don't see what you're looking for here, please don't hesitate to reach out!)
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
|
||||||
|
You can submit glitch-soc-specific translations via [Crowdin](https://crowdin.com/project/glitch-soc). They are periodically merged into the codebase.
|
||||||
|
|
||||||
|
[![Crowdin](https://badges.crowdin.net/glitch-soc/localized.svg)](https://crowdin.com/project/glitch-soc)
|
||||||
|
|
||||||
## Planning ##
|
## Planning ##
|
||||||
|
|
||||||
Right now a lot of the planning for this project takes place in our development Discord, or through GitHub Issues and Projects.
|
Right now a lot of the planning for this project takes place in our development Discord, or through GitHub Issues and Projects.
|
||||||
|
|
17
Gemfile
17
Gemfile
|
@ -10,7 +10,7 @@ gem 'puma', '~> 5.6'
|
||||||
gem 'rails', '~> 6.1.7'
|
gem 'rails', '~> 6.1.7'
|
||||||
gem 'sprockets', '~> 3.7.2'
|
gem 'sprockets', '~> 3.7.2'
|
||||||
gem 'thor', '~> 1.2'
|
gem 'thor', '~> 1.2'
|
||||||
gem 'rack', '~> 2.2.4'
|
gem 'rack', '~> 2.2.6'
|
||||||
|
|
||||||
gem 'hamlit-rails', '~> 0.2'
|
gem 'hamlit-rails', '~> 0.2'
|
||||||
gem 'pg', '~> 1.4'
|
gem 'pg', '~> 1.4'
|
||||||
|
@ -19,7 +19,7 @@ gem 'pghero', '~> 2.8'
|
||||||
gem 'dotenv-rails', '~> 2.8'
|
gem 'dotenv-rails', '~> 2.8'
|
||||||
|
|
||||||
gem 'aws-sdk-s3', '~> 1.117', require: false
|
gem 'aws-sdk-s3', '~> 1.117', require: false
|
||||||
gem 'fog-core', '<= 2.1.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.1'
|
gem 'kt-paperclip', '~> 7.1'
|
||||||
gem 'blurhash', '~> 0.1'
|
gem 'blurhash', '~> 0.1'
|
||||||
|
@ -51,7 +51,7 @@ gem 'ed25519', '~> 1.3'
|
||||||
gem 'fast_blank', '~> 1.0'
|
gem 'fast_blank', '~> 1.0'
|
||||||
gem 'fastimage'
|
gem 'fastimage'
|
||||||
gem 'hiredis', '~> 0.6'
|
gem 'hiredis', '~> 0.6'
|
||||||
gem 'redis-namespace', '~> 1.9'
|
gem 'redis-namespace', '~> 1.10'
|
||||||
gem 'htmlentities', '~> 4.3'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'http', '~> 5.1'
|
gem 'http', '~> 5.1'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
|
@ -60,14 +60,14 @@ gem 'idn-ruby', require: 'idn'
|
||||||
gem 'kaminari', '~> 1.2'
|
gem 'kaminari', '~> 1.2'
|
||||||
gem 'link_header', '~> 0.0'
|
gem 'link_header', '~> 0.0'
|
||||||
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
|
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
|
||||||
gem 'nokogiri', '~> 1.13'
|
gem 'nokogiri', '~> 1.14'
|
||||||
gem 'nsa', '~> 0.2'
|
gem 'nsa', '~> 0.2'
|
||||||
gem 'oj', '~> 3.13'
|
gem 'oj', '~> 3.13'
|
||||||
gem 'ox', '~> 2.14'
|
gem 'ox', '~> 2.14'
|
||||||
gem 'parslet'
|
gem 'parslet'
|
||||||
gem 'posix-spawn'
|
gem 'posix-spawn'
|
||||||
gem 'public_suffix', '~> 5.0'
|
gem 'public_suffix', '~> 5.0'
|
||||||
gem 'pundit', '~> 2.2'
|
gem 'pundit', '~> 2.3'
|
||||||
gem 'premailer-rails'
|
gem 'premailer-rails'
|
||||||
gem 'rack-attack', '~> 6.6'
|
gem 'rack-attack', '~> 6.6'
|
||||||
gem 'rack-cors', '~> 1.1', require: 'rack/cors'
|
gem 'rack-cors', '~> 1.1', require: 'rack/cors'
|
||||||
|
@ -79,7 +79,7 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||||
gem 'rqrcode', '~> 2.1'
|
gem 'rqrcode', '~> 2.1'
|
||||||
gem 'ruby-progressbar', '~> 1.11'
|
gem 'ruby-progressbar', '~> 1.11'
|
||||||
gem 'sanitize', '~> 6.0'
|
gem 'sanitize', '~> 6.0'
|
||||||
gem 'scenic', '~> 1.6'
|
gem 'scenic', '~> 1.7'
|
||||||
gem 'sidekiq', '~> 6.5'
|
gem 'sidekiq', '~> 6.5'
|
||||||
gem 'sidekiq-scheduler', '~> 4.0'
|
gem 'sidekiq-scheduler', '~> 4.0'
|
||||||
gem 'sidekiq-unique-jobs', '~> 7.1'
|
gem 'sidekiq-unique-jobs', '~> 7.1'
|
||||||
|
@ -120,14 +120,13 @@ end
|
||||||
group :test do
|
group :test do
|
||||||
gem 'capybara', '~> 3.38'
|
gem 'capybara', '~> 3.38'
|
||||||
gem 'climate_control', '~> 0.2'
|
gem 'climate_control', '~> 0.2'
|
||||||
gem 'faker', '~> 3.0'
|
gem 'faker', '~> 3.1'
|
||||||
gem 'json-schema', '~> 3.0'
|
gem 'json-schema', '~> 3.0'
|
||||||
gem 'microformats', '~> 4.4'
|
|
||||||
gem 'rack-test', '~> 2.0'
|
gem 'rack-test', '~> 2.0'
|
||||||
gem 'rails-controller-testing', '~> 1.0'
|
gem 'rails-controller-testing', '~> 1.0'
|
||||||
gem 'rspec_junit_formatter', '~> 0.6'
|
gem 'rspec_junit_formatter', '~> 0.6'
|
||||||
gem 'rspec-sidekiq', '~> 3.1'
|
gem 'rspec-sidekiq', '~> 3.1'
|
||||||
gem 'simplecov', '~> 0.21', require: false
|
gem 'simplecov', '~> 0.22', require: false
|
||||||
gem 'webmock', '~> 3.18'
|
gem 'webmock', '~> 3.18'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
212
Gemfile.lock
212
Gemfile.lock
|
@ -10,40 +10,40 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (6.1.7)
|
actioncable (6.1.7.1)
|
||||||
actionpack (= 6.1.7)
|
actionpack (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (6.1.7)
|
actionmailbox (6.1.7.1)
|
||||||
actionpack (= 6.1.7)
|
actionpack (= 6.1.7.1)
|
||||||
activejob (= 6.1.7)
|
activejob (= 6.1.7.1)
|
||||||
activerecord (= 6.1.7)
|
activerecord (= 6.1.7.1)
|
||||||
activestorage (= 6.1.7)
|
activestorage (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
actionmailer (6.1.7)
|
actionmailer (6.1.7.1)
|
||||||
actionpack (= 6.1.7)
|
actionpack (= 6.1.7.1)
|
||||||
actionview (= 6.1.7)
|
actionview (= 6.1.7.1)
|
||||||
activejob (= 6.1.7)
|
activejob (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (6.1.7)
|
actionpack (6.1.7.1)
|
||||||
actionview (= 6.1.7)
|
actionview (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
rack (~> 2.0, >= 2.0.9)
|
rack (~> 2.0, >= 2.0.9)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (6.1.7)
|
actiontext (6.1.7.1)
|
||||||
actionpack (= 6.1.7)
|
actionpack (= 6.1.7.1)
|
||||||
activerecord (= 6.1.7)
|
activerecord (= 6.1.7.1)
|
||||||
activestorage (= 6.1.7)
|
activestorage (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (6.1.7)
|
actionview (6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
|
@ -54,22 +54,22 @@ GEM
|
||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
active_record_query_trace (1.8)
|
active_record_query_trace (1.8)
|
||||||
activejob (6.1.7)
|
activejob (6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (6.1.7)
|
activemodel (6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
activerecord (6.1.7)
|
activerecord (6.1.7.1)
|
||||||
activemodel (= 6.1.7)
|
activemodel (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
activestorage (6.1.7)
|
activestorage (6.1.7.1)
|
||||||
actionpack (= 6.1.7)
|
actionpack (= 6.1.7.1)
|
||||||
activejob (= 6.1.7)
|
activejob (= 6.1.7.1)
|
||||||
activerecord (= 6.1.7)
|
activerecord (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
mini_mime (>= 1.1.0)
|
mini_mime (>= 1.1.0)
|
||||||
activesupport (6.1.7)
|
activesupport (6.1.7.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
|
@ -130,7 +130,7 @@ GEM
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||||
redis (>= 1.0, < 6)
|
redis (>= 1.0, < 6)
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
bullet (7.0.4)
|
bullet (7.0.7)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
uniform_notifier (~> 1.11)
|
uniform_notifier (~> 1.11)
|
||||||
bundler-audit (0.9.1)
|
bundler-audit (0.9.1)
|
||||||
|
@ -184,6 +184,7 @@ GEM
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
css_parser (1.12.0)
|
css_parser (1.12.0)
|
||||||
addressable
|
addressable
|
||||||
|
date (3.3.3)
|
||||||
debug_inspector (1.0.0)
|
debug_inspector (1.0.0)
|
||||||
devise (4.8.1)
|
devise (4.8.1)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
|
@ -203,7 +204,7 @@ GEM
|
||||||
diff-lcs (1.5.0)
|
diff-lcs (1.5.0)
|
||||||
discard (1.2.1)
|
discard (1.2.1)
|
||||||
activerecord (>= 4.2, < 8)
|
activerecord (>= 4.2, < 8)
|
||||||
docile (1.3.4)
|
docile (1.4.0)
|
||||||
domain_name (0.5.20190701)
|
domain_name (0.5.20190701)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
doorkeeper (5.6.2)
|
doorkeeper (5.6.2)
|
||||||
|
@ -223,12 +224,12 @@ GEM
|
||||||
faraday (~> 1)
|
faraday (~> 1)
|
||||||
multi_json
|
multi_json
|
||||||
encryptor (3.0.0)
|
encryptor (3.0.0)
|
||||||
erubi (1.11.0)
|
erubi (1.12.0)
|
||||||
et-orbi (1.2.7)
|
et-orbi (1.2.7)
|
||||||
tzinfo
|
tzinfo
|
||||||
excon (0.76.0)
|
excon (0.95.0)
|
||||||
fabrication (2.30.0)
|
fabrication (2.30.0)
|
||||||
faker (3.0.0)
|
faker (3.1.0)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (1.9.3)
|
faraday (1.9.3)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
|
@ -271,7 +272,7 @@ GEM
|
||||||
fog-core (>= 1.45, <= 2.1.0)
|
fog-core (>= 1.45, <= 2.1.0)
|
||||||
fog-json (>= 1.0)
|
fog-json (>= 1.0)
|
||||||
ipaddress (>= 0.8)
|
ipaddress (>= 0.8)
|
||||||
formatador (0.2.5)
|
formatador (0.3.0)
|
||||||
fugit (1.7.1)
|
fugit (1.7.1)
|
||||||
et-orbi (~> 1, >= 1.2.7)
|
et-orbi (~> 1, >= 1.2.7)
|
||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
|
@ -282,7 +283,7 @@ GEM
|
||||||
addressable (~> 2.7)
|
addressable (~> 2.7)
|
||||||
omniauth (>= 1.9, < 3)
|
omniauth (>= 1.9, < 3)
|
||||||
openid_connect (~> 1.2)
|
openid_connect (~> 1.2)
|
||||||
globalid (1.0.0)
|
globalid (1.0.1)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 5.0)
|
||||||
hamlit (2.13.0)
|
hamlit (2.13.0)
|
||||||
temple (>= 0.8.2)
|
temple (>= 0.8.2)
|
||||||
|
@ -301,7 +302,7 @@ GEM
|
||||||
hiredis (0.6.3)
|
hiredis (0.6.3)
|
||||||
hkdf (0.3.0)
|
hkdf (0.3.0)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
http (5.1.0)
|
http (5.1.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
http-cookie (~> 1.0)
|
http-cookie (~> 1.0)
|
||||||
http-form_data (~> 2.2)
|
http-form_data (~> 2.2)
|
||||||
|
@ -330,9 +331,9 @@ GEM
|
||||||
idn-ruby (0.1.5)
|
idn-ruby (0.1.5)
|
||||||
ipaddress (0.8.3)
|
ipaddress (0.8.3)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.6.2)
|
json (2.6.3)
|
||||||
json-canonicalization (0.3.0)
|
json-canonicalization (0.3.0)
|
||||||
json-jwt (1.13.0)
|
json-jwt (1.14.0)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
aes_key_wrap
|
aes_key_wrap
|
||||||
bindata
|
bindata
|
||||||
|
@ -349,7 +350,7 @@ GEM
|
||||||
json-schema (3.0.0)
|
json-schema (3.0.0)
|
||||||
addressable (>= 2.8)
|
addressable (>= 2.8)
|
||||||
jsonapi-renderer (0.2.2)
|
jsonapi-renderer (0.2.2)
|
||||||
jwt (2.4.1)
|
jwt (2.5.0)
|
||||||
kaminari (1.2.2)
|
kaminari (1.2.2)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
kaminari-actionview (= 1.2.2)
|
kaminari-actionview (= 1.2.2)
|
||||||
|
@ -389,8 +390,11 @@ GEM
|
||||||
loofah (2.19.1)
|
loofah (2.19.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.7.1)
|
mail (2.8.0.1)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
|
net-imap
|
||||||
|
net-pop
|
||||||
|
net-smtp
|
||||||
makara (0.5.1)
|
makara (0.5.1)
|
||||||
activerecord (>= 5.2.0)
|
activerecord (>= 5.2.0)
|
||||||
marcel (1.0.2)
|
marcel (1.0.2)
|
||||||
|
@ -399,19 +403,21 @@ GEM
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
memory_profiler (1.0.1)
|
memory_profiler (1.0.1)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
microformats (4.4.1)
|
|
||||||
json (~> 2.2)
|
|
||||||
nokogiri (~> 1.10)
|
|
||||||
mime-types (3.4.1)
|
mime-types (3.4.1)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2022.0105)
|
mime-types-data (3.2022.0105)
|
||||||
mini_mime (1.1.2)
|
mini_mime (1.1.2)
|
||||||
mini_portile2 (2.8.0)
|
mini_portile2 (2.8.1)
|
||||||
minitest (5.16.3)
|
minitest (5.17.0)
|
||||||
msgpack (1.6.0)
|
msgpack (1.6.0)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
|
net-imap (0.3.4)
|
||||||
|
date
|
||||||
|
net-protocol
|
||||||
net-ldap (0.17.1)
|
net-ldap (0.17.1)
|
||||||
|
net-pop (0.1.2)
|
||||||
|
net-protocol
|
||||||
net-protocol (0.1.3)
|
net-protocol (0.1.3)
|
||||||
timeout
|
timeout
|
||||||
net-scp (4.0.0.rc1)
|
net-scp (4.0.0.rc1)
|
||||||
|
@ -420,7 +426,7 @@ GEM
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ssh (7.0.1)
|
net-ssh (7.0.1)
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.13.10)
|
nokogiri (1.14.0)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nsa (0.2.8)
|
nsa (0.2.8)
|
||||||
|
@ -456,9 +462,9 @@ GEM
|
||||||
openssl-signature_algorithm (1.2.1)
|
openssl-signature_algorithm (1.2.1)
|
||||||
openssl (> 2.0, < 3.1)
|
openssl (> 2.0, < 3.1)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ox (2.14.11)
|
ox (2.14.13)
|
||||||
parallel (1.22.1)
|
parallel (1.22.1)
|
||||||
parser (3.1.2.1)
|
parser (3.2.0.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
parslet (2.0.0)
|
parslet (2.0.0)
|
||||||
pastel (0.8.0)
|
pastel (0.8.0)
|
||||||
|
@ -488,11 +494,11 @@ GEM
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.1)
|
||||||
puma (5.6.5)
|
puma (5.6.5)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.2.0)
|
pundit (2.3.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.6.1)
|
racc (1.6.2)
|
||||||
rack (2.2.4)
|
rack (2.2.6.2)
|
||||||
rack-attack (6.6.1)
|
rack-attack (6.6.1)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rack-cors (1.1.1)
|
rack-cors (1.1.1)
|
||||||
|
@ -507,20 +513,20 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-test (2.0.2)
|
rack-test (2.0.2)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rails (6.1.7)
|
rails (6.1.7.1)
|
||||||
actioncable (= 6.1.7)
|
actioncable (= 6.1.7.1)
|
||||||
actionmailbox (= 6.1.7)
|
actionmailbox (= 6.1.7.1)
|
||||||
actionmailer (= 6.1.7)
|
actionmailer (= 6.1.7.1)
|
||||||
actionpack (= 6.1.7)
|
actionpack (= 6.1.7.1)
|
||||||
actiontext (= 6.1.7)
|
actiontext (= 6.1.7.1)
|
||||||
actionview (= 6.1.7)
|
actionview (= 6.1.7.1)
|
||||||
activejob (= 6.1.7)
|
activejob (= 6.1.7.1)
|
||||||
activemodel (= 6.1.7)
|
activemodel (= 6.1.7.1)
|
||||||
activerecord (= 6.1.7)
|
activerecord (= 6.1.7.1)
|
||||||
activestorage (= 6.1.7)
|
activestorage (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 6.1.7)
|
railties (= 6.1.7.1)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.5)
|
rails-controller-testing (1.0.5)
|
||||||
actionpack (>= 5.0.1.rc1)
|
actionpack (>= 5.0.1.rc1)
|
||||||
|
@ -536,9 +542,9 @@ GEM
|
||||||
railties (>= 6.0.0, < 7)
|
railties (>= 6.0.0, < 7)
|
||||||
rails-settings-cached (0.6.6)
|
rails-settings-cached (0.6.6)
|
||||||
rails (>= 4.2.0)
|
rails (>= 4.2.0)
|
||||||
railties (6.1.7)
|
railties (6.1.7.1)
|
||||||
actionpack (= 6.1.7)
|
actionpack (= 6.1.7.1)
|
||||||
activesupport (= 6.1.7)
|
activesupport (= 6.1.7.1)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
|
@ -550,11 +556,11 @@ GEM
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
redcarpet (3.5.1)
|
redcarpet (3.5.1)
|
||||||
redis (4.5.1)
|
redis (4.5.1)
|
||||||
redis-namespace (1.9.0)
|
redis-namespace (1.10.0)
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
redis (>= 3.0.0, < 6.0)
|
redis (>= 3.0.0, < 6.0)
|
||||||
regexp_parser (2.6.0)
|
regexp_parser (2.6.1)
|
||||||
request_store (1.5.1)
|
request_store (1.5.1)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
responders (3.0.1)
|
responders (3.0.1)
|
||||||
|
@ -589,27 +595,30 @@ GEM
|
||||||
rspec-support (3.11.1)
|
rspec-support (3.11.1)
|
||||||
rspec_junit_formatter (0.6.0)
|
rspec_junit_formatter (0.6.0)
|
||||||
rspec-core (>= 2, < 4, != 2.12.0)
|
rspec-core (>= 2, < 4, != 2.12.0)
|
||||||
rubocop (1.39.0)
|
rubocop (1.43.0)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.1.2.1)
|
parser (>= 3.2.0.0)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 1.8, < 3.0)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rexml (>= 3.2.5, < 4.0)
|
rexml (>= 3.2.5, < 4.0)
|
||||||
rubocop-ast (>= 1.23.0, < 2.0)
|
rubocop-ast (>= 1.24.1, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 1.4.0, < 3.0)
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
rubocop-ast (1.23.0)
|
rubocop-ast (1.24.1)
|
||||||
parser (>= 3.1.1.0)
|
parser (>= 3.1.1.0)
|
||||||
rubocop-performance (1.15.1)
|
rubocop-capybara (2.17.0)
|
||||||
|
rubocop (~> 1.41)
|
||||||
|
rubocop-performance (1.15.2)
|
||||||
rubocop (>= 1.7.0, < 2.0)
|
rubocop (>= 1.7.0, < 2.0)
|
||||||
rubocop-ast (>= 0.4.0)
|
rubocop-ast (>= 0.4.0)
|
||||||
rubocop-rails (2.17.2)
|
rubocop-rails (2.17.4)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.33.0, < 2.0)
|
rubocop (>= 1.33.0, < 2.0)
|
||||||
rubocop-rspec (2.15.0)
|
rubocop-rspec (2.18.0)
|
||||||
rubocop (~> 1.33)
|
rubocop (~> 1.33)
|
||||||
|
rubocop-capybara
|
||||||
ruby-progressbar (1.11.0)
|
ruby-progressbar (1.11.0)
|
||||||
ruby-saml (1.13.0)
|
ruby-saml (1.13.0)
|
||||||
nokogiri (>= 1.10.5)
|
nokogiri (>= 1.10.5)
|
||||||
|
@ -622,7 +631,7 @@ GEM
|
||||||
sanitize (6.0.0)
|
sanitize (6.0.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
scenic (1.6.0)
|
scenic (1.7.0)
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
semantic_range (3.0.0)
|
semantic_range (3.0.0)
|
||||||
|
@ -648,12 +657,12 @@ GEM
|
||||||
simple_form (5.1.0)
|
simple_form (5.1.0)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
activemodel (>= 5.2)
|
activemodel (>= 5.2)
|
||||||
simplecov (0.21.2)
|
simplecov (0.22.0)
|
||||||
docile (~> 1.1)
|
docile (~> 1.1)
|
||||||
simplecov-html (~> 0.11)
|
simplecov-html (~> 0.11)
|
||||||
simplecov_json_formatter (~> 0.1)
|
simplecov_json_formatter (~> 0.1)
|
||||||
simplecov-html (0.12.3)
|
simplecov-html (0.12.3)
|
||||||
simplecov_json_formatter (0.1.2)
|
simplecov_json_formatter (0.1.4)
|
||||||
smart_properties (1.17.0)
|
smart_properties (1.17.0)
|
||||||
sprockets (3.7.2)
|
sprockets (3.7.2)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
|
@ -707,7 +716,7 @@ GEM
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.8.2)
|
unf_ext (0.0.8.2)
|
||||||
unicode-display_width (2.3.0)
|
unicode-display_width (2.4.2)
|
||||||
uniform_notifier (1.16.0)
|
uniform_notifier (1.16.0)
|
||||||
validate_email (0.1.6)
|
validate_email (0.1.6)
|
||||||
activemodel (>= 3.0)
|
activemodel (>= 3.0)
|
||||||
|
@ -784,10 +793,10 @@ DEPENDENCIES
|
||||||
dotenv-rails (~> 2.8)
|
dotenv-rails (~> 2.8)
|
||||||
ed25519 (~> 1.3)
|
ed25519 (~> 1.3)
|
||||||
fabrication (~> 2.30)
|
fabrication (~> 2.30)
|
||||||
faker (~> 3.0)
|
faker (~> 3.1)
|
||||||
fast_blank (~> 1.0)
|
fast_blank (~> 1.0)
|
||||||
fastimage
|
fastimage
|
||||||
fog-core (<= 2.1.0)
|
fog-core (<= 2.4.0)
|
||||||
fog-openstack (~> 0.3)
|
fog-openstack (~> 0.3)
|
||||||
fuubar (~> 2.5)
|
fuubar (~> 2.5)
|
||||||
gitlab-omniauth-openid-connect (~> 0.10.0)
|
gitlab-omniauth-openid-connect (~> 0.10.0)
|
||||||
|
@ -812,10 +821,9 @@ DEPENDENCIES
|
||||||
makara (~> 0.5)
|
makara (~> 0.5)
|
||||||
mario-redis-lock (~> 1.2)
|
mario-redis-lock (~> 1.2)
|
||||||
memory_profiler
|
memory_profiler
|
||||||
microformats (~> 4.4)
|
|
||||||
mime-types (~> 3.4.1)
|
mime-types (~> 3.4.1)
|
||||||
net-ldap (~> 0.17)
|
net-ldap (~> 0.17)
|
||||||
nokogiri (~> 1.13)
|
nokogiri (~> 1.14)
|
||||||
nsa (~> 0.2)
|
nsa (~> 0.2)
|
||||||
oj (~> 3.13)
|
oj (~> 3.13)
|
||||||
omniauth (~> 1.9)
|
omniauth (~> 1.9)
|
||||||
|
@ -834,8 +842,8 @@ DEPENDENCIES
|
||||||
pry-rails (~> 0.3)
|
pry-rails (~> 0.3)
|
||||||
public_suffix (~> 5.0)
|
public_suffix (~> 5.0)
|
||||||
puma (~> 5.6)
|
puma (~> 5.6)
|
||||||
pundit (~> 2.2)
|
pundit (~> 2.3)
|
||||||
rack (~> 2.2.4)
|
rack (~> 2.2.6)
|
||||||
rack-attack (~> 6.6)
|
rack-attack (~> 6.6)
|
||||||
rack-cors (~> 1.1)
|
rack-cors (~> 1.1)
|
||||||
rack-test (~> 2.0)
|
rack-test (~> 2.0)
|
||||||
|
@ -846,7 +854,7 @@ DEPENDENCIES
|
||||||
rdf-normalize (~> 0.5)
|
rdf-normalize (~> 0.5)
|
||||||
redcarpet (~> 3.5)
|
redcarpet (~> 3.5)
|
||||||
redis (~> 4.5)
|
redis (~> 4.5)
|
||||||
redis-namespace (~> 1.9)
|
redis-namespace (~> 1.10)
|
||||||
rexml (~> 3.2)
|
rexml (~> 3.2)
|
||||||
rqrcode (~> 2.1)
|
rqrcode (~> 2.1)
|
||||||
rspec-rails (~> 5.1)
|
rspec-rails (~> 5.1)
|
||||||
|
@ -858,14 +866,14 @@ DEPENDENCIES
|
||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
ruby-progressbar (~> 1.11)
|
ruby-progressbar (~> 1.11)
|
||||||
sanitize (~> 6.0)
|
sanitize (~> 6.0)
|
||||||
scenic (~> 1.6)
|
scenic (~> 1.7)
|
||||||
sidekiq (~> 6.5)
|
sidekiq (~> 6.5)
|
||||||
sidekiq-bulk (~> 0.2.0)
|
sidekiq-bulk (~> 0.2.0)
|
||||||
sidekiq-scheduler (~> 4.0)
|
sidekiq-scheduler (~> 4.0)
|
||||||
sidekiq-unique-jobs (~> 7.1)
|
sidekiq-unique-jobs (~> 7.1)
|
||||||
simple-navigation (~> 4.4)
|
simple-navigation (~> 4.4)
|
||||||
simple_form (~> 5.1)
|
simple_form (~> 5.1)
|
||||||
simplecov (~> 0.21)
|
simplecov (~> 0.22)
|
||||||
sprockets (~> 3.7.2)
|
sprockets (~> 3.7.2)
|
||||||
sprockets-rails (~> 3.4)
|
sprockets-rails (~> 3.4)
|
||||||
stackprof
|
stackprof
|
||||||
|
@ -880,9 +888,3 @@ DEPENDENCIES
|
||||||
webpacker (~> 5.4)
|
webpacker (~> 5.4)
|
||||||
webpush!
|
webpush!
|
||||||
xorcist (~> 1.1)
|
xorcist (~> 1.1)
|
||||||
|
|
||||||
RUBY VERSION
|
|
||||||
ruby 3.0.4p208
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
2.2.33
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ module Admin
|
||||||
account_action.save!
|
account_action.save!
|
||||||
|
|
||||||
if account_action.with_report?
|
if account_action.with_report?
|
||||||
redirect_to admin_reports_path
|
redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: params[:report_id])
|
||||||
else
|
else
|
||||||
redirect_to admin_account_path(@account.id)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,9 +23,7 @@ module Admin
|
||||||
@import = Admin::Import.new(import_params)
|
@import = Admin::Import.new(import_params)
|
||||||
return render :new unless @import.validate
|
return render :new unless @import.validate
|
||||||
|
|
||||||
parse_import_data!(export_headers)
|
@import.csv_rows.each do |row|
|
||||||
|
|
||||||
@data.take(Admin::Import::ROWS_PROCESSING_LIMIT).each do |row|
|
|
||||||
domain = row['#domain'].strip
|
domain = row['#domain'].strip
|
||||||
next if DomainAllow.allowed?(domain)
|
next if DomainAllow.allowed?(domain)
|
||||||
|
|
||||||
|
|
|
@ -23,24 +23,30 @@ module Admin
|
||||||
@import = Admin::Import.new(import_params)
|
@import = Admin::Import.new(import_params)
|
||||||
return render :new unless @import.validate
|
return render :new unless @import.validate
|
||||||
|
|
||||||
parse_import_data!(export_headers)
|
|
||||||
|
|
||||||
@global_private_comment = I18n.t('admin.export_domain_blocks.import.private_comment_template', source: @import.data_file_name, date: I18n.l(Time.now.utc))
|
@global_private_comment = I18n.t('admin.export_domain_blocks.import.private_comment_template', source: @import.data_file_name, date: I18n.l(Time.now.utc))
|
||||||
|
|
||||||
@form = Form::DomainBlockBatch.new
|
@form = Form::DomainBlockBatch.new
|
||||||
@domain_blocks = @data.take(Admin::Import::ROWS_PROCESSING_LIMIT).filter_map do |row|
|
@domain_blocks = @import.csv_rows.filter_map do |row|
|
||||||
domain = row['#domain'].strip
|
domain = row['#domain'].strip
|
||||||
next if DomainBlock.rule_for(domain).present?
|
next if DomainBlock.rule_for(domain).present?
|
||||||
|
|
||||||
domain_block = DomainBlock.new(domain: domain,
|
domain_block = DomainBlock.new(domain: domain,
|
||||||
severity: row['#severity'].strip,
|
severity: row.fetch('#severity', :suspend),
|
||||||
reject_media: row['#reject_media'].strip,
|
reject_media: row.fetch('#reject_media', false),
|
||||||
reject_reports: row['#reject_reports'].strip,
|
reject_reports: row.fetch('#reject_reports', false),
|
||||||
private_comment: @global_private_comment,
|
private_comment: @global_private_comment,
|
||||||
public_comment: row['#public_comment']&.strip,
|
public_comment: row['#public_comment'],
|
||||||
obfuscate: row['#obfuscate'].strip)
|
obfuscate: row.fetch('#obfuscate', false))
|
||||||
|
|
||||||
domain_block if domain_block.valid?
|
if domain_block.invalid?
|
||||||
|
flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: domain_block.errors.full_messages.join(', '))
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
domain_block
|
||||||
|
rescue ArgumentError => e
|
||||||
|
flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: e.message)
|
||||||
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
@warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
|
@warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
|
||||||
|
|
|
@ -49,7 +49,7 @@ module Admin
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_instance
|
def set_instance
|
||||||
@instance = Instance.find(params[:id])
|
@instance = Instance.find(TagManager.instance.normalize_domain(params[:id]&.strip))
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_instances
|
def set_instances
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
class Admin::Reports::ActionsController < Admin::BaseController
|
class Admin::Reports::ActionsController < Admin::BaseController
|
||||||
before_action :set_report
|
before_action :set_report
|
||||||
|
|
||||||
|
def preview
|
||||||
|
authorize @report, :show?
|
||||||
|
@moderation_action = action_from_button
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @report, :show?
|
authorize @report, :show?
|
||||||
|
|
||||||
|
@ -13,7 +18,8 @@ class Admin::Reports::ActionsController < Admin::BaseController
|
||||||
status_ids: @report.status_ids,
|
status_ids: @report.status_ids,
|
||||||
current_account: current_account,
|
current_account: current_account,
|
||||||
report_id: @report.id,
|
report_id: @report.id,
|
||||||
send_email_notification: !@report.spam?
|
send_email_notification: !@report.spam?,
|
||||||
|
text: params[:text]
|
||||||
)
|
)
|
||||||
|
|
||||||
status_batch_action.save!
|
status_batch_action.save!
|
||||||
|
@ -23,13 +29,16 @@ class Admin::Reports::ActionsController < Admin::BaseController
|
||||||
report_id: @report.id,
|
report_id: @report.id,
|
||||||
target_account: @report.target_account,
|
target_account: @report.target_account,
|
||||||
current_account: current_account,
|
current_account: current_account,
|
||||||
send_email_notification: !@report.spam?
|
send_email_notification: !@report.spam?,
|
||||||
|
text: params[:text]
|
||||||
)
|
)
|
||||||
|
|
||||||
account_action.save!
|
account_action.save!
|
||||||
|
else
|
||||||
|
return redirect_to admin_report_path(@report), alert: I18n.t('admin.reports.unknown_action_msg', action: action_from_button)
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to admin_reports_path
|
redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: @report.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -47,6 +56,8 @@ class Admin::Reports::ActionsController < Admin::BaseController
|
||||||
'silence'
|
'silence'
|
||||||
elsif params[:suspend]
|
elsif params[:suspend]
|
||||||
'suspend'
|
'suspend'
|
||||||
|
elsif params[:moderation_action]
|
||||||
|
params[:moderation_action]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,17 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def account_params
|
def account_params
|
||||||
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
|
params.permit(
|
||||||
|
:display_name,
|
||||||
|
:note,
|
||||||
|
:avatar,
|
||||||
|
:header,
|
||||||
|
:locked,
|
||||||
|
:bot,
|
||||||
|
:discoverable,
|
||||||
|
:hide_collections,
|
||||||
|
fields_attributes: [:name, :value]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_settings_params
|
def user_settings_params
|
||||||
|
|
|
@ -3,6 +3,14 @@
|
||||||
class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
|
class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
|
||||||
before_action -> { authorize_if_got_token! :'admin:read' }
|
before_action -> { authorize_if_got_token! :'admin:read' }
|
||||||
|
|
||||||
|
def index
|
||||||
|
if current_user&.can?(:manage_taxonomies)
|
||||||
|
render json: @tags, each_serializer: REST::Admin::TagSerializer
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def enabled?
|
def enabled?
|
||||||
|
|
|
@ -80,6 +80,7 @@ class Api::V1::StatusesController < Api::BaseController
|
||||||
current_account.id,
|
current_account.id,
|
||||||
text: status_params[:status],
|
text: status_params[:status],
|
||||||
media_ids: status_params[:media_ids],
|
media_ids: status_params[:media_ids],
|
||||||
|
media_attributes: status_params[:media_attributes],
|
||||||
sensitive: status_params[:sensitive],
|
sensitive: status_params[:sensitive],
|
||||||
language: status_params[:language],
|
language: status_params[:language],
|
||||||
spoiler_text: status_params[:spoiler_text],
|
spoiler_text: status_params[:spoiler_text],
|
||||||
|
@ -131,6 +132,12 @@ class Api::V1::StatusesController < Api::BaseController
|
||||||
:scheduled_at,
|
:scheduled_at,
|
||||||
:content_type,
|
:content_type,
|
||||||
media_ids: [],
|
media_ids: [],
|
||||||
|
media_attributes: [
|
||||||
|
:id,
|
||||||
|
:thumbnail,
|
||||||
|
:description,
|
||||||
|
:focus,
|
||||||
|
],
|
||||||
poll: [
|
poll: [
|
||||||
:multiple,
|
:multiple,
|
||||||
:hide_totals,
|
:hide_totals,
|
||||||
|
|
|
@ -26,14 +26,4 @@ module AdminExportControllerConcern
|
||||||
def import_params
|
def import_params
|
||||||
params.require(:admin_import).permit(:data)
|
params.require(:admin_import).permit(:data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def import_data_path
|
|
||||||
params[:admin_import][:data].path
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_import_data!(default_headers)
|
|
||||||
data = CSV.read(import_data_path, headers: true, encoding: 'UTF-8')
|
|
||||||
data = CSV.read(import_data_path, headers: default_headers, encoding: 'UTF-8') unless data.headers&.first&.strip&.include?(default_headers[0])
|
|
||||||
@data = data.reject(&:blank?)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,11 +46,11 @@ module SignatureVerification
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_account_signature!
|
def require_account_signature!
|
||||||
render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
|
render json: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_actor_signature!
|
def require_actor_signature!
|
||||||
render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_actor
|
render json: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_actor
|
||||||
end
|
end
|
||||||
|
|
||||||
def signed_request?
|
def signed_request?
|
||||||
|
@ -97,11 +97,11 @@ module SignatureVerification
|
||||||
|
|
||||||
actor = stoplight_wrap_request { actor_refresh_key!(actor) }
|
actor = stoplight_wrap_request { actor_refresh_key!(actor) }
|
||||||
|
|
||||||
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
|
raise SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil?
|
||||||
|
|
||||||
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
||||||
|
|
||||||
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)"
|
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
|
||||||
rescue SignatureVerificationError => e
|
rescue SignatureVerificationError => e
|
||||||
fail_with! e.message
|
fail_with! e.message
|
||||||
rescue HTTP::Error, OpenSSL::SSL::SSLError => e
|
rescue HTTP::Error, OpenSSL::SSL::SSLError => e
|
||||||
|
@ -118,8 +118,8 @@ module SignatureVerification
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fail_with!(message)
|
def fail_with!(message, **options)
|
||||||
@signature_verification_failure_reason = message
|
@signature_verification_failure_reason = { error: message }.merge(options)
|
||||||
@signed_request_actor = nil
|
@signed_request_actor = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -209,8 +209,8 @@ module SignatureVerification
|
||||||
end
|
end
|
||||||
|
|
||||||
expires_time = Time.at(signature_params['expires'].to_i).utc if signature_params['expires'].present?
|
expires_time = Time.at(signature_params['expires'].to_i).utc if signature_params['expires'].present?
|
||||||
rescue ArgumentError
|
rescue ArgumentError => e
|
||||||
return false
|
raise SignatureVerificationError, "Invalid Date header: #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
expires_time ||= created_time + 5.minutes unless created_time.nil?
|
expires_time ||= created_time + 5.minutes unless created_time.nil?
|
||||||
|
|
|
@ -4,22 +4,17 @@ module WebAppControllerConcern
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
|
prepend_before_action :redirect_unauthenticated_to_permalinks!
|
||||||
before_action :set_pack
|
before_action :set_pack
|
||||||
before_action :redirect_unauthenticated_to_permalinks!
|
|
||||||
before_action :set_app_body_class
|
before_action :set_app_body_class
|
||||||
before_action :set_referrer_policy_header
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_app_body_class
|
def set_app_body_class
|
||||||
@body_classes = 'app-body'
|
@body_classes = 'app-body'
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_referrer_policy_header
|
|
||||||
response.headers['Referrer-Policy'] = 'origin'
|
|
||||||
end
|
|
||||||
|
|
||||||
def redirect_unauthenticated_to_permalinks!
|
def redirect_unauthenticated_to_permalinks!
|
||||||
return if user_signed_in?
|
return if user_signed_in? # NOTE: Different from upstream because we allow moved users to log in
|
||||||
|
|
||||||
redirect_path = PermalinkRedirector.new(request.path).redirect_path
|
redirect_path = PermalinkRedirector.new(request.path).redirect_path
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ ready(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector('a#add-instance-button')?.addEventListener('click', (e) => {
|
document.querySelector('a#add-instance-button')?.addEventListener('click', (e) => {
|
||||||
const domain = document.getElementById('by_domain')?.value;
|
const domain = document.querySelector('input[type="text"]#by_domain')?.value;
|
||||||
|
|
||||||
if (domain) {
|
if (domain) {
|
||||||
const url = new URL(event.target.href);
|
const url = new URL(event.target.href);
|
||||||
|
|
|
@ -181,6 +181,18 @@ export function submitCompose(routerHistory) {
|
||||||
|
|
||||||
dispatch(submitComposeRequest());
|
dispatch(submitComposeRequest());
|
||||||
|
|
||||||
|
// If we're editing a post with media attachments, those have not
|
||||||
|
// necessarily been changed on the server. Do it now in the same
|
||||||
|
// API call.
|
||||||
|
let media_attributes;
|
||||||
|
if (statusId !== null) {
|
||||||
|
media_attributes = media.map(item => ({
|
||||||
|
id: item.get('id'),
|
||||||
|
description: item.get('description'),
|
||||||
|
focus: item.get('focus'),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
api(getState).request({
|
api(getState).request({
|
||||||
url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
|
url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
|
||||||
method: statusId === null ? 'post' : 'put',
|
method: statusId === null ? 'post' : 'put',
|
||||||
|
@ -189,6 +201,7 @@ export function submitCompose(routerHistory) {
|
||||||
content_type: getState().getIn(['compose', 'content_type']),
|
content_type: getState().getIn(['compose', 'content_type']),
|
||||||
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
||||||
media_ids: media.map(item => item.get('id')),
|
media_ids: media.map(item => item.get('id')),
|
||||||
|
media_attributes,
|
||||||
sensitive: getState().getIn(['compose', 'sensitive']) || (spoilerText.length > 0 && media.size !== 0),
|
sensitive: getState().getIn(['compose', 'sensitive']) || (spoilerText.length > 0 && media.size !== 0),
|
||||||
spoiler_text: spoilerText,
|
spoiler_text: spoilerText,
|
||||||
visibility: getState().getIn(['compose', 'privacy']),
|
visibility: getState().getIn(['compose', 'privacy']),
|
||||||
|
@ -415,11 +428,31 @@ export function changeUploadCompose(id, params) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(changeUploadComposeRequest());
|
dispatch(changeUploadComposeRequest());
|
||||||
|
|
||||||
api(getState).put(`/api/v1/media/${id}`, params).then(response => {
|
let media = getState().getIn(['compose', 'media_attachments']).find((item) => item.get('id') === id);
|
||||||
dispatch(changeUploadComposeSuccess(response.data));
|
|
||||||
}).catch(error => {
|
// Editing already-attached media is deferred to editing the post itself.
|
||||||
dispatch(changeUploadComposeFail(id, error));
|
// For simplicity's sake, fake an API reply.
|
||||||
});
|
if (media && !media.get('unattached')) {
|
||||||
|
let { description, focus } = params;
|
||||||
|
const data = media.toJS();
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
data.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focus) {
|
||||||
|
focus = focus.split(',');
|
||||||
|
data.meta = { focus: { x: parseFloat(focus[0]), y: parseFloat(focus[1]) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(changeUploadComposeSuccess(data, true));
|
||||||
|
} else {
|
||||||
|
api(getState).put(`/api/v1/media/${id}`, params).then(response => {
|
||||||
|
dispatch(changeUploadComposeSuccess(response.data, false));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(changeUploadComposeFail(id, error));
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -430,10 +463,11 @@ export function changeUploadComposeRequest() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function changeUploadComposeSuccess(media) {
|
export function changeUploadComposeSuccess(media, attached) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||||
media: media,
|
media: media,
|
||||||
|
attached: attached,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
|
export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
|
||||||
export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
|
export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
|
||||||
|
|
||||||
export function openDropdownMenu(id, placement, keyboard, scroll_key) {
|
export function openDropdownMenu(id, keyboard, scroll_key) {
|
||||||
return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard, scroll_key };
|
return { type: DROPDOWN_MENU_OPEN, id, keyboard, scroll_key };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function closeDropdownMenu(id) {
|
export function closeDropdownMenu(id) {
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
import api from '../api';
|
import api, { getLinks } from '../api';
|
||||||
|
|
||||||
export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST';
|
export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST';
|
||||||
export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS';
|
export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS';
|
||||||
export const HASHTAG_FETCH_FAIL = 'HASHTAG_FETCH_FAIL';
|
export const HASHTAG_FETCH_FAIL = 'HASHTAG_FETCH_FAIL';
|
||||||
|
|
||||||
|
export const FOLLOWED_HASHTAGS_FETCH_REQUEST = 'FOLLOWED_HASHTAGS_FETCH_REQUEST';
|
||||||
|
export const FOLLOWED_HASHTAGS_FETCH_SUCCESS = 'FOLLOWED_HASHTAGS_FETCH_SUCCESS';
|
||||||
|
export const FOLLOWED_HASHTAGS_FETCH_FAIL = 'FOLLOWED_HASHTAGS_FETCH_FAIL';
|
||||||
|
|
||||||
|
export const FOLLOWED_HASHTAGS_EXPAND_REQUEST = 'FOLLOWED_HASHTAGS_EXPAND_REQUEST';
|
||||||
|
export const FOLLOWED_HASHTAGS_EXPAND_SUCCESS = 'FOLLOWED_HASHTAGS_EXPAND_SUCCESS';
|
||||||
|
export const FOLLOWED_HASHTAGS_EXPAND_FAIL = 'FOLLOWED_HASHTAGS_EXPAND_FAIL';
|
||||||
|
|
||||||
export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST';
|
export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST';
|
||||||
export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS';
|
export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS';
|
||||||
export const HASHTAG_FOLLOW_FAIL = 'HASHTAG_FOLLOW_FAIL';
|
export const HASHTAG_FOLLOW_FAIL = 'HASHTAG_FOLLOW_FAIL';
|
||||||
|
@ -37,6 +45,78 @@ export const fetchHashtagFail = error => ({
|
||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const fetchFollowedHashtags = () => (dispatch, getState) => {
|
||||||
|
dispatch(fetchFollowedHashtagsRequest());
|
||||||
|
|
||||||
|
api(getState).get('/api/v1/followed_tags').then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
dispatch(fetchFollowedHashtagsSuccess(response.data, next ? next.uri : null));
|
||||||
|
}).catch(err => {
|
||||||
|
dispatch(fetchFollowedHashtagsFail(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fetchFollowedHashtagsRequest() {
|
||||||
|
return {
|
||||||
|
type: FOLLOWED_HASHTAGS_FETCH_REQUEST,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fetchFollowedHashtagsSuccess(followed_tags, next) {
|
||||||
|
return {
|
||||||
|
type: FOLLOWED_HASHTAGS_FETCH_SUCCESS,
|
||||||
|
followed_tags,
|
||||||
|
next,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fetchFollowedHashtagsFail(error) {
|
||||||
|
return {
|
||||||
|
type: FOLLOWED_HASHTAGS_FETCH_FAIL,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandFollowedHashtags() {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const url = getState().getIn(['followed_tags', 'next']);
|
||||||
|
|
||||||
|
if (url === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(expandFollowedHashtagsRequest());
|
||||||
|
|
||||||
|
api(getState).get(url).then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
dispatch(expandFollowedHashtagsSuccess(response.data, next ? next.uri : null));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(expandFollowedHashtagsFail(error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandFollowedHashtagsRequest() {
|
||||||
|
return {
|
||||||
|
type: FOLLOWED_HASHTAGS_EXPAND_REQUEST,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandFollowedHashtagsSuccess(followed_tags, next) {
|
||||||
|
return {
|
||||||
|
type: FOLLOWED_HASHTAGS_EXPAND_SUCCESS,
|
||||||
|
followed_tags,
|
||||||
|
next,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandFollowedHashtagsFail(error) {
|
||||||
|
return {
|
||||||
|
type: FOLLOWED_HASHTAGS_EXPAND_FAIL,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const followHashtag = name => (dispatch, getState) => {
|
export const followHashtag = name => (dispatch, getState) => {
|
||||||
dispatch(followHashtagRequest(name));
|
dispatch(followHashtagRequest(name));
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default class Trends extends React.PureComponent {
|
||||||
<Hashtag
|
<Hashtag
|
||||||
key={hashtag.name}
|
key={hashtag.name}
|
||||||
name={hashtag.name}
|
name={hashtag.name}
|
||||||
href={`/admin/tags/${hashtag.id}`}
|
href={hashtag.id === undefined ? undefined : `/admin/tags/${hashtag.id}`}
|
||||||
people={hashtag.history[0].accounts * 1 + hashtag.history[1].accounts * 1}
|
people={hashtag.history[0].accounts * 1 + hashtag.history[1].accounts * 1}
|
||||||
uses={hashtag.history[0].uses * 1 + hashtag.history[1].uses * 1}
|
uses={hashtag.history[0].uses * 1 + hashtag.history[1].uses * 1}
|
||||||
history={hashtag.history.reverse().map(day => day.uses)}
|
history={hashtag.history.reverse().map(day => day.uses)}
|
||||||
|
|
|
@ -2,9 +2,7 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import IconButton from './icon_button';
|
import IconButton from './icon_button';
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
import Motion from '../features/ui/util/optional_motion';
|
|
||||||
import spring from 'react-motion/lib/spring';
|
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CircularProgress } from 'flavours/glitch/components/loading_indicator';
|
import { CircularProgress } from 'flavours/glitch/components/loading_indicator';
|
||||||
|
@ -24,9 +22,6 @@ class DropdownMenu extends React.PureComponent {
|
||||||
scrollable: PropTypes.bool,
|
scrollable: PropTypes.bool,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
placement: PropTypes.string,
|
|
||||||
arrowOffsetLeft: PropTypes.string,
|
|
||||||
arrowOffsetTop: PropTypes.string,
|
|
||||||
openedViaKeyboard: PropTypes.bool,
|
openedViaKeyboard: PropTypes.bool,
|
||||||
renderItem: PropTypes.func,
|
renderItem: PropTypes.func,
|
||||||
renderHeader: PropTypes.func,
|
renderHeader: PropTypes.func,
|
||||||
|
@ -35,11 +30,6 @@ class DropdownMenu extends React.PureComponent {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
style: {},
|
style: {},
|
||||||
placement: 'bottom',
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
mounted: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDocumentClick = e => {
|
handleDocumentClick = e => {
|
||||||
|
@ -56,8 +46,6 @@ class DropdownMenu extends React.PureComponent {
|
||||||
if (this.focusedItem && this.props.openedViaKeyboard) {
|
if (this.focusedItem && this.props.openedViaKeyboard) {
|
||||||
this.focusedItem.focus({ preventScroll: true });
|
this.focusedItem.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ mounted: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
|
@ -139,40 +127,28 @@ class DropdownMenu extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { items, style, placement, arrowOffsetLeft, arrowOffsetTop, scrollable, renderHeader, loading } = this.props;
|
const { items, scrollable, renderHeader, loading } = this.props;
|
||||||
const { mounted } = this.state;
|
|
||||||
|
|
||||||
let renderItem = this.props.renderItem || this.renderItem;
|
let renderItem = this.props.renderItem || this.renderItem;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
<div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })} ref={this.setRef}>
|
||||||
{({ opacity, scaleX, scaleY }) => (
|
{loading && (
|
||||||
// It should not be transformed when mounting because the resulting
|
<CircularProgress size={30} strokeWidth={3.5} />
|
||||||
// size will be used to determine the coordinate of the menu by
|
)}
|
||||||
// react-overlays
|
|
||||||
<div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
|
||||||
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
|
||||||
|
|
||||||
<div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })}>
|
{!loading && renderHeader && (
|
||||||
{loading && (
|
<div className='dropdown-menu__container__header'>
|
||||||
<CircularProgress size={30} strokeWidth={3.5} />
|
{renderHeader(items)}
|
||||||
)}
|
|
||||||
|
|
||||||
{!loading && renderHeader && (
|
|
||||||
<div className='dropdown-menu__container__header'>
|
|
||||||
{renderHeader(items)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!loading && (
|
|
||||||
<ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}>
|
|
||||||
{items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Motion>
|
|
||||||
|
{!loading && (
|
||||||
|
<ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}>
|
||||||
|
{items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +173,6 @@ export default class Dropdown extends React.PureComponent {
|
||||||
isUserTouching: PropTypes.func,
|
isUserTouching: PropTypes.func,
|
||||||
onOpen: PropTypes.func.isRequired,
|
onOpen: PropTypes.func.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
dropdownPlacement: PropTypes.string,
|
|
||||||
openDropdownId: PropTypes.number,
|
openDropdownId: PropTypes.number,
|
||||||
openedViaKeyboard: PropTypes.bool,
|
openedViaKeyboard: PropTypes.bool,
|
||||||
renderItem: PropTypes.func,
|
renderItem: PropTypes.func,
|
||||||
|
@ -213,13 +188,11 @@ export default class Dropdown extends React.PureComponent {
|
||||||
id: id++,
|
id: id++,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = ({ target, type }) => {
|
handleClick = ({ type }) => {
|
||||||
if (this.state.id === this.props.openDropdownId) {
|
if (this.state.id === this.props.openDropdownId) {
|
||||||
this.handleClose();
|
this.handleClose();
|
||||||
} else {
|
} else {
|
||||||
const { top } = target.getBoundingClientRect();
|
this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click');
|
||||||
const placement = top * 2 < innerHeight ? 'bottom' : 'top';
|
|
||||||
this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +276,6 @@ export default class Dropdown extends React.PureComponent {
|
||||||
disabled,
|
disabled,
|
||||||
loading,
|
loading,
|
||||||
scrollable,
|
scrollable,
|
||||||
dropdownPlacement,
|
|
||||||
openDropdownId,
|
openDropdownId,
|
||||||
openedViaKeyboard,
|
openedViaKeyboard,
|
||||||
children,
|
children,
|
||||||
|
@ -314,7 +286,6 @@ export default class Dropdown extends React.PureComponent {
|
||||||
const open = this.state.id === openDropdownId;
|
const open = this.state.id === openDropdownId;
|
||||||
|
|
||||||
const button = children ? React.cloneElement(React.Children.only(children), {
|
const button = children ? React.cloneElement(React.Children.only(children), {
|
||||||
ref: this.setTargetRef,
|
|
||||||
onClick: this.handleClick,
|
onClick: this.handleClick,
|
||||||
onMouseDown: this.handleMouseDown,
|
onMouseDown: this.handleMouseDown,
|
||||||
onKeyDown: this.handleButtonKeyDown,
|
onKeyDown: this.handleButtonKeyDown,
|
||||||
|
@ -326,7 +297,6 @@ export default class Dropdown extends React.PureComponent {
|
||||||
active={open}
|
active={open}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
size={size}
|
size={size}
|
||||||
ref={this.setTargetRef}
|
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
onKeyDown={this.handleButtonKeyDown}
|
onKeyDown={this.handleButtonKeyDown}
|
||||||
|
@ -336,19 +306,27 @@ export default class Dropdown extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{button}
|
<span ref={this.setTargetRef}>
|
||||||
|
{button}
|
||||||
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
|
</span>
|
||||||
<DropdownMenu
|
<Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||||
items={items}
|
{({ props, arrowProps, placement }) => (
|
||||||
loading={loading}
|
<div {...props}>
|
||||||
scrollable={scrollable}
|
<div className={`dropdown-animation dropdown-menu ${placement}`}>
|
||||||
onClose={this.handleClose}
|
<div className={`dropdown-menu__arrow ${placement}`} {...arrowProps} />
|
||||||
openedViaKeyboard={openedViaKeyboard}
|
<DropdownMenu
|
||||||
renderItem={renderItem}
|
items={items}
|
||||||
renderHeader={renderHeader}
|
loading={loading}
|
||||||
onItemClick={this.handleItemClick}
|
scrollable={scrollable}
|
||||||
/>
|
onClose={this.handleClose}
|
||||||
|
openedViaKeyboard={openedViaKeyboard}
|
||||||
|
renderItem={renderItem}
|
||||||
|
renderHeader={renderHeader}
|
||||||
|
onItemClick={this.handleItemClick}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { fetchHistory } from 'flavours/glitch/actions/history';
|
||||||
import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
|
import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
|
||||||
|
|
||||||
const mapStateToProps = (state, { statusId }) => ({
|
const mapStateToProps = (state, { statusId }) => ({
|
||||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
|
||||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
||||||
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
||||||
items: state.getIn(['history', statusId, 'items']),
|
items: state.getIn(['history', statusId, 'items']),
|
||||||
|
@ -13,9 +12,9 @@ const mapStateToProps = (state, { statusId }) => ({
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { statusId }) => ({
|
const mapDispatchToProps = (dispatch, { statusId }) => ({
|
||||||
|
|
||||||
onOpen (id, onItemClick, dropdownPlacement, keyboard) {
|
onOpen (id, onItemClick, keyboard) {
|
||||||
dispatch(fetchHistory(statusId));
|
dispatch(fetchHistory(statusId));
|
||||||
dispatch(openDropdownMenu(id, dropdownPlacement, keyboard));
|
dispatch(openDropdownMenu(id, keyboard));
|
||||||
},
|
},
|
||||||
|
|
||||||
onClose (id) {
|
onClose (id) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ export default class IconButton extends React.PureComponent {
|
||||||
counter: PropTypes.number,
|
counter: PropTypes.number,
|
||||||
obfuscateCount: PropTypes.bool,
|
obfuscateCount: PropTypes.bool,
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
|
ariaHidden: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -39,6 +40,7 @@ export default class IconButton extends React.PureComponent {
|
||||||
animate: false,
|
animate: false,
|
||||||
overlay: false,
|
overlay: false,
|
||||||
tabIndex: '0',
|
tabIndex: '0',
|
||||||
|
ariaHidden: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -115,6 +117,7 @@ export default class IconButton extends React.PureComponent {
|
||||||
counter,
|
counter,
|
||||||
obfuscateCount,
|
obfuscateCount,
|
||||||
href,
|
href,
|
||||||
|
ariaHidden,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -155,6 +158,7 @@ export default class IconButton extends React.PureComponent {
|
||||||
<button
|
<button
|
||||||
aria-label={title}
|
aria-label={title}
|
||||||
aria-expanded={expanded}
|
aria-expanded={expanded}
|
||||||
|
aria-hidden={ariaHidden}
|
||||||
title={title}
|
title={title}
|
||||||
className={classes}
|
className={classes}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
|
|
|
@ -376,7 +376,7 @@ class MediaGallery extends React.PureComponent {
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
} else if (visible) {
|
} else if (visible) {
|
||||||
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} />;
|
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />;
|
||||||
} else {
|
} else {
|
||||||
spoilerButton = (
|
spoilerButton = (
|
||||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
||||||
|
|
|
@ -106,7 +106,7 @@ class Status extends ImmutablePureComponent {
|
||||||
scrollKey: PropTypes.string,
|
scrollKey: PropTypes.string,
|
||||||
deployPictureInPicture: PropTypes.func,
|
deployPictureInPicture: PropTypes.func,
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
pictureInPicture: PropTypes.shape({
|
pictureInPicture: ImmutablePropTypes.contains({
|
||||||
inUse: PropTypes.bool,
|
inUse: PropTypes.bool,
|
||||||
available: PropTypes.bool,
|
available: PropTypes.bool,
|
||||||
}),
|
}),
|
||||||
|
@ -607,7 +607,7 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
attachments = status.get('media_attachments');
|
attachments = status.get('media_attachments');
|
||||||
|
|
||||||
if (pictureInPicture.inUse) {
|
if (pictureInPicture.get('inUse')) {
|
||||||
media.push(<PictureInPicturePlaceholder width={this.props.cachedMediaWidth} />);
|
media.push(<PictureInPicturePlaceholder width={this.props.cachedMediaWidth} />);
|
||||||
mediaIcons.push('video-camera');
|
mediaIcons.push('video-camera');
|
||||||
} else if (attachments.size > 0) {
|
} else if (attachments.size > 0) {
|
||||||
|
@ -635,7 +635,7 @@ class Status extends ImmutablePureComponent {
|
||||||
width={this.props.cachedMediaWidth}
|
width={this.props.cachedMediaWidth}
|
||||||
height={110}
|
height={110}
|
||||||
cacheWidth={this.props.cacheMediaWidth}
|
cacheWidth={this.props.cacheMediaWidth}
|
||||||
deployPictureInPicture={pictureInPicture.available ? this.handleDeployPictureInPicture : undefined}
|
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
||||||
sensitive={status.get('sensitive')}
|
sensitive={status.get('sensitive')}
|
||||||
blurhash={attachment.get('blurhash')}
|
blurhash={attachment.get('blurhash')}
|
||||||
visible={this.state.showMedia}
|
visible={this.state.showMedia}
|
||||||
|
@ -664,7 +664,7 @@ class Status extends ImmutablePureComponent {
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
width={this.props.cachedMediaWidth}
|
width={this.props.cachedMediaWidth}
|
||||||
cacheWidth={this.props.cacheMediaWidth}
|
cacheWidth={this.props.cacheMediaWidth}
|
||||||
deployPictureInPicture={pictureInPicture.available ? this.handleDeployPictureInPicture : undefined}
|
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
||||||
visible={this.state.showMedia}
|
visible={this.state.showMedia}
|
||||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||||
/>)}
|
/>)}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import RelativeTimestamp from './relative_timestamp';
|
||||||
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions';
|
import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions';
|
||||||
|
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||||
import EmojiPickerDropdown from '../features/compose/containers/emoji_picker_dropdown_container';
|
import EmojiPickerDropdown from '../features/compose/containers/emoji_picker_dropdown_container';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -39,9 +40,10 @@ const messages = defineMessages({
|
||||||
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
|
||||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||||
hide: { id: 'status.hide', defaultMessage: 'Hide toot' },
|
copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
|
||||||
|
hide: { id: 'status.hide', defaultMessage: 'Hide post' },
|
||||||
edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
|
edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
|
||||||
filter: { id: 'status.filter', defaultMessage: 'Filter this post' },
|
filter: { id: 'status.filter', defaultMessage: 'Filter this post' },
|
||||||
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
||||||
|
@ -206,8 +208,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
||||||
const { signedIn } = this.context.identity;
|
const { permissions } = this.context.identity;
|
||||||
|
|
||||||
const anonymousAccess = !me;
|
const anonymousAccess = !me;
|
||||||
const mutingConversation = status.get('muted');
|
const mutingConversation = status.get('muted');
|
||||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||||
|
@ -262,19 +263,19 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
|
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
||||||
|
|
||||||
if ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) {
|
if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
if (accountAdminLink !== undefined) {
|
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||||
menu.push({
|
if (accountAdminLink !== undefined) {
|
||||||
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) });
|
||||||
href: accountAdminLink(status.getIn(['account', 'id'])),
|
}
|
||||||
});
|
if (statusAdminLink !== undefined) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (statusAdminLink !== undefined) {
|
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
||||||
menu.push({
|
const domain = status.getIn(['account', 'acct']).split('@')[1];
|
||||||
text: intl.formatMessage(messages.admin_status),
|
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
|
||||||
href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +309,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.hide)} icon='eye' onClick={this.handleHideClick} />
|
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.hide)} icon='eye' onClick={this.handleHideClick} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const canReact = signedIn && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
|
const canReact = permissions && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
|
||||||
const reactButton = (
|
const reactButton = (
|
||||||
<IconButton
|
<IconButton
|
||||||
className='status__action-bar-button'
|
className='status__action-bar-button'
|
||||||
|
@ -332,7 +333,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon={reblogIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon={reblogIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||||
{
|
{
|
||||||
signedIn
|
permissions
|
||||||
? <EmojiPickerDropdown className='status__action-bar-button' onPickEmoji={this.handleEmojiPick} button={reactButton} disabled={!canReact} />
|
? <EmojiPickerDropdown className='status__action-bar-button' onPickEmoji={this.handleEmojiPick} button={reactButton} disabled={!canReact} />
|
||||||
: reactButton
|
: reactButton
|
||||||
}
|
}
|
||||||
|
@ -354,6 +355,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='status__action-bar-spacer' />
|
||||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>
|
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>
|
||||||
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
|
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -5,18 +5,17 @@ import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
|
||||||
import { isUserTouching } from '../is_mobile';
|
import { isUserTouching } from '../is_mobile';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
|
||||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
||||||
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
|
const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
|
||||||
onOpen(id, onItemClick, dropdownPlacement, keyboard) {
|
onOpen(id, onItemClick, keyboard) {
|
||||||
dispatch(isUserTouching() ? openModal('ACTIONS', {
|
dispatch(isUserTouching() ? openModal('ACTIONS', {
|
||||||
status,
|
status,
|
||||||
actions: items,
|
actions: items,
|
||||||
onClick: onItemClick,
|
onClick: onItemClick,
|
||||||
}) : openDropdownMenu(id, dropdownPlacement, keyboard, scrollKey));
|
}) : openDropdownMenu(id, keyboard, scrollKey));
|
||||||
},
|
},
|
||||||
|
|
||||||
onClose(id) {
|
onClose(id) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Status from 'flavours/glitch/components/status';
|
import Status from 'flavours/glitch/components/status';
|
||||||
import { makeGetStatus } from 'flavours/glitch/selectors';
|
import { makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors';
|
||||||
import {
|
import {
|
||||||
replyCompose,
|
replyCompose,
|
||||||
mentionCompose,
|
mentionCompose,
|
||||||
|
@ -61,6 +61,7 @@ const messages = defineMessages({
|
||||||
|
|
||||||
const makeMapStateToProps = () => {
|
const makeMapStateToProps = () => {
|
||||||
const getStatus = makeGetStatus();
|
const getStatus = makeGetStatus();
|
||||||
|
const getPictureInPicture = makeGetPictureInPicture();
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
|
|
||||||
|
@ -84,11 +85,7 @@ const makeMapStateToProps = () => {
|
||||||
account: account || props.account,
|
account: account || props.account,
|
||||||
settings: state.get('local_settings'),
|
settings: state.get('local_settings'),
|
||||||
prepend: prepend || props.prepend,
|
prepend: prepend || props.prepend,
|
||||||
|
pictureInPicture: getPictureInPicture(state, props),
|
||||||
pictureInPicture: {
|
|
||||||
inUse: state.getIn(['meta', 'layout']) !== 'mobile' && state.get('picture_in_picture').statusId === props.id,
|
|
||||||
available: state.getIn(['meta', 'layout']) !== 'mobile',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
|
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
|
||||||
import 'intersection-observer';
|
import 'intersection-observer';
|
||||||
import 'requestidlecallback';
|
import 'requestidlecallback';
|
||||||
import objectFitImages from 'object-fit-images';
|
|
||||||
|
|
||||||
objectFitImages();
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { NavLink } from 'react-router-dom';
|
||||||
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
||||||
import AccountNoteContainer from '../containers/account_note_container';
|
import AccountNoteContainer from '../containers/account_note_container';
|
||||||
import FollowRequestNoteContainer from '../containers/follow_request_note_container';
|
import FollowRequestNoteContainer from '../containers/follow_request_note_container';
|
||||||
import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions';
|
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -45,6 +45,7 @@ const messages = defineMessages({
|
||||||
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: 'Favourites' },
|
||||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||||
|
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' },
|
||||||
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' },
|
||||||
|
@ -52,6 +53,7 @@ const messages = defineMessages({
|
||||||
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
|
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
|
||||||
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
|
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
|
||||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||||
|
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||||
add_account_note: { id: 'account.add_account_note', defaultMessage: 'Add note for @{name}' },
|
add_account_note: { id: 'account.add_account_note', defaultMessage: 'Add note for @{name}' },
|
||||||
languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
|
languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
|
||||||
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
||||||
|
@ -155,7 +157,7 @@ class Header extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, hidden, intl, domain } = this.props;
|
const { account, hidden, intl, domain } = this.props;
|
||||||
const { signedIn } = this.context.identity;
|
const { signedIn, permissions } = this.context.identity;
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -187,7 +189,7 @@ class Header extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
|
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
|
||||||
bellBtn = <IconButton icon='bell-o' size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me !== account.get('id')) {
|
if (me !== account.get('id')) {
|
||||||
|
@ -244,6 +246,7 @@ class Header extends ImmutablePureComponent {
|
||||||
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
||||||
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
||||||
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
||||||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||||
|
@ -291,9 +294,14 @@ class Header extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.get('id') !== me && (this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) {
|
if (account.get('id') !== me && ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: accountAdminLink(account.get('id')) });
|
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: accountAdminLink(account.get('id')) });
|
||||||
|
}
|
||||||
|
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: remoteDomain }), href: `/admin/instances/${remoteDomain}` });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = { __html: account.get('note_emojified') };
|
const content = { __html: account.get('note_emojified') };
|
||||||
|
|
|
@ -12,6 +12,7 @@ const messages = defineMessages({
|
||||||
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: 'Favourites' },
|
||||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||||
|
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' },
|
||||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
|
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
|
||||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||||
|
@ -46,6 +47,7 @@ class ActionBar extends React.PureComponent {
|
||||||
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
||||||
menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
|
menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
|
||||||
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
||||||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||||
|
|
|
@ -310,7 +310,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
|
|
||||||
<ReplyIndicatorContainer />
|
<ReplyIndicatorContainer />
|
||||||
|
|
||||||
<div className={`spoiler-input ${spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}>
|
<div className={`spoiler-input ${spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}>
|
||||||
<AutosuggestInput
|
<AutosuggestInput
|
||||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||||
value={spoilerText}
|
value={spoilerText}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
|
|
||||||
// Components.
|
// Components.
|
||||||
import IconButton from 'flavours/glitch/components/icon_button';
|
import IconButton from 'flavours/glitch/components/icon_button';
|
||||||
|
@ -45,7 +45,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Toggles opening and closing the dropdown.
|
// Toggles opening and closing the dropdown.
|
||||||
handleToggle = ({ target, type }) => {
|
handleToggle = ({ type }) => {
|
||||||
const { onModalOpen } = this.props;
|
const { onModalOpen } = this.props;
|
||||||
const { open } = this.state;
|
const { open } = this.state;
|
||||||
|
|
||||||
|
@ -59,11 +59,9 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { top } = target.getBoundingClientRect();
|
|
||||||
if (this.state.open && this.activeElement) {
|
if (this.state.open && this.activeElement) {
|
||||||
this.activeElement.focus({ preventScroll: true });
|
this.activeElement.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
|
|
||||||
this.setState({ open: !this.state.open, openedViaKeyboard: type !== 'click' });
|
this.setState({ open: !this.state.open, openedViaKeyboard: type !== 'click' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +156,18 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTargetRef = c => {
|
||||||
|
this.target = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
findTarget = () => {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOverlayEnter = (state) => {
|
||||||
|
this.setState({ placement: state.placement });
|
||||||
|
}
|
||||||
|
|
||||||
// Rendering.
|
// Rendering.
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
|
@ -179,6 +189,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
||||||
<div
|
<div
|
||||||
className={classNames('privacy-dropdown', placement, { active: open })}
|
className={classNames('privacy-dropdown', placement, { active: open })}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
|
ref={this.setTargetRef}
|
||||||
>
|
>
|
||||||
<div className={classNames('privacy-dropdown__value', { active })}>
|
<div className={classNames('privacy-dropdown__value', { active })}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -204,18 +215,26 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
||||||
containerPadding={20}
|
containerPadding={20}
|
||||||
placement={placement}
|
placement={placement}
|
||||||
show={open}
|
show={open}
|
||||||
target={this}
|
flip
|
||||||
|
target={this.findTarget}
|
||||||
container={container}
|
container={container}
|
||||||
|
popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}
|
||||||
>
|
>
|
||||||
<DropdownMenu
|
{({ props, placement }) => (
|
||||||
items={items}
|
<div {...props}>
|
||||||
renderItemContents={renderItemContents}
|
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
|
||||||
onChange={onChange}
|
<DropdownMenu
|
||||||
onClose={this.handleClose}
|
items={items}
|
||||||
value={value}
|
renderItemContents={renderItemContents}
|
||||||
openedViaKeyboard={this.state.openedViaKeyboard}
|
onChange={onChange}
|
||||||
closeOnChange={closeOnChange}
|
onClose={this.handleClose}
|
||||||
/>
|
value={value}
|
||||||
|
openedViaKeyboard={this.state.openedViaKeyboard}
|
||||||
|
closeOnChange={closeOnChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Package imports.
|
// Package imports.
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import spring from 'react-motion/lib/spring';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
@ -10,15 +9,8 @@ import Icon from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
// Utils.
|
// Utils.
|
||||||
import { withPassive } from 'flavours/glitch/utils/dom_helpers';
|
import { withPassive } from 'flavours/glitch/utils/dom_helpers';
|
||||||
import Motion from '../../ui/util/optional_motion';
|
|
||||||
import { assignHandlers } from 'flavours/glitch/utils/react_helpers';
|
import { assignHandlers } from 'flavours/glitch/utils/react_helpers';
|
||||||
|
|
||||||
// The spring to use with our motion.
|
|
||||||
const springMotion = spring(1, {
|
|
||||||
damping: 35,
|
|
||||||
stiffness: 400,
|
|
||||||
});
|
|
||||||
|
|
||||||
// The component.
|
// The component.
|
||||||
export default class ComposerOptionsDropdownContent extends React.PureComponent {
|
export default class ComposerOptionsDropdownContent extends React.PureComponent {
|
||||||
|
|
||||||
|
@ -44,7 +36,6 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
mounted: false,
|
|
||||||
value: this.props.openedViaKeyboard ? this.props.items[0].name : undefined,
|
value: this.props.openedViaKeyboard ? this.props.items[0].name : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +47,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores our node in `this.node`.
|
// Stores our node in `this.node`.
|
||||||
handleRef = (node) => {
|
setRef = (node) => {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +60,6 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
|
||||||
} else {
|
} else {
|
||||||
this.node.firstChild.focus({ preventScroll: true });
|
this.node.firstChild.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
this.setState({ mounted: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// On unmounting, we remove our listeners.
|
// On unmounting, we remove our listeners.
|
||||||
|
@ -191,7 +181,6 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
|
||||||
|
|
||||||
// Rendering.
|
// Rendering.
|
||||||
render () {
|
render () {
|
||||||
const { mounted } = this.state;
|
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
onChange,
|
onChange,
|
||||||
|
@ -201,36 +190,9 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
|
||||||
|
|
||||||
// The result.
|
// The result.
|
||||||
return (
|
return (
|
||||||
<Motion
|
<div style={{ ...style }} role='listbox' ref={this.setRef}>
|
||||||
defaultStyle={{
|
{!!items && items.map((item, i) => this.renderItem(item, i))}
|
||||||
opacity: 0,
|
</div>
|
||||||
scaleX: 0.85,
|
|
||||||
scaleY: 0.75,
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
opacity: springMotion,
|
|
||||||
scaleX: springMotion,
|
|
||||||
scaleY: springMotion,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ opacity, scaleX, scaleY }) => (
|
|
||||||
// It should not be transformed when mounting because the resulting
|
|
||||||
// size will be used to determine the coordinate of the menu by
|
|
||||||
// react-overlays
|
|
||||||
<div
|
|
||||||
className='privacy-dropdown__dropdown'
|
|
||||||
ref={this.handleRef}
|
|
||||||
role='listbox'
|
|
||||||
style={{
|
|
||||||
...style,
|
|
||||||
opacity: opacity,
|
|
||||||
transform: mounted ? `scale(${scaleX}, ${scaleY})` : null,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!!items && items.map((item, i) => this.renderItem(item, i))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Motion>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
|
@ -155,9 +155,6 @@ class EmojiPickerMenu extends React.PureComponent {
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onPick: PropTypes.func.isRequired,
|
onPick: PropTypes.func.isRequired,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
placement: PropTypes.string,
|
|
||||||
arrowOffsetLeft: PropTypes.string,
|
|
||||||
arrowOffsetTop: PropTypes.string,
|
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
skinTone: PropTypes.number.isRequired,
|
skinTone: PropTypes.number.isRequired,
|
||||||
onSkinTone: PropTypes.func.isRequired,
|
onSkinTone: PropTypes.func.isRequired,
|
||||||
|
@ -327,14 +324,13 @@ class EmojiPickerDropdown extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
active: false,
|
active: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
placement: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setRef = (c) => {
|
setRef = (c) => {
|
||||||
this.dropdown = c;
|
this.dropdown = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
onShowDropdown = ({ target }) => {
|
onShowDropdown = () => {
|
||||||
this.setState({ active: true });
|
this.setState({ active: true });
|
||||||
|
|
||||||
if (!EmojiPicker) {
|
if (!EmojiPicker) {
|
||||||
|
@ -349,9 +345,6 @@ class EmojiPickerDropdown extends React.PureComponent {
|
||||||
this.setState({ loading: false, active: false });
|
this.setState({ loading: false, active: false });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { top } = target.getBoundingClientRect();
|
|
||||||
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onHideDropdown = () => {
|
onHideDropdown = () => {
|
||||||
|
@ -385,7 +378,7 @@ class EmojiPickerDropdown extends React.PureComponent {
|
||||||
render () {
|
render () {
|
||||||
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
|
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
|
||||||
const title = intl.formatMessage(messages.emoji);
|
const title = intl.formatMessage(messages.emoji);
|
||||||
const { active, loading, placement } = this.state;
|
const { active, loading } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
|
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
|
||||||
|
@ -397,16 +390,22 @@ class EmojiPickerDropdown extends React.PureComponent {
|
||||||
/>}
|
/>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Overlay show={active} placement={placement} target={this.findTarget}>
|
<Overlay show={active} placement={'bottom'} target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||||
<EmojiPickerMenu
|
{({ props, placement })=> (
|
||||||
custom_emojis={this.props.custom_emojis}
|
<div {...props} style={{ ...props.style, width: 299 }}>
|
||||||
loading={loading}
|
<div className={`dropdown-animation ${placement}`}>
|
||||||
onClose={this.onHideDropdown}
|
<EmojiPickerMenu
|
||||||
onPick={onPickEmoji}
|
custom_emojis={this.props.custom_emojis}
|
||||||
onSkinTone={onSkinTone}
|
loading={loading}
|
||||||
skinTone={skinTone}
|
onClose={this.onHideDropdown}
|
||||||
frequentlyUsedEmojis={frequentlyUsedEmojis}
|
onPick={onPickEmoji}
|
||||||
/>
|
onSkinTone={onSkinTone}
|
||||||
|
skinTone={skinTone}
|
||||||
|
frequentlyUsedEmojis={frequentlyUsedEmojis}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,9 +2,7 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
import TextIconButton from './text_icon_button';
|
import TextIconButton from './text_icon_button';
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
import Motion from 'flavours/glitch/features/ui/util/optional_motion';
|
|
||||||
import spring from 'react-motion/lib/spring';
|
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
import { languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
||||||
|
@ -22,10 +20,8 @@ const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||||
class LanguageDropdownMenu extends React.PureComponent {
|
class LanguageDropdownMenu extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
style: PropTypes.object,
|
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired,
|
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
placement: PropTypes.string.isRequired,
|
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
|
languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
|
||||||
|
@ -37,7 +33,6 @@ class LanguageDropdownMenu extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
mounted: false,
|
|
||||||
searchValue: '',
|
searchValue: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,7 +45,6 @@ class LanguageDropdownMenu extends React.PureComponent {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.addEventListener('click', this.handleDocumentClick, false);
|
document.addEventListener('click', this.handleDocumentClick, false);
|
||||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||||
this.setState({ mounted: true });
|
|
||||||
|
|
||||||
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
|
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
|
||||||
// to wait for a frame before focusing
|
// to wait for a frame before focusing
|
||||||
|
@ -222,29 +216,22 @@ class LanguageDropdownMenu extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { style, placement, intl } = this.props;
|
const { intl } = this.props;
|
||||||
const { mounted, searchValue } = this.state;
|
const { searchValue } = this.state;
|
||||||
const isSearching = searchValue !== '';
|
const isSearching = searchValue !== '';
|
||||||
const results = this.search();
|
const results = this.search();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
<div ref={this.setRef}>
|
||||||
{({ opacity, scaleX, scaleY }) => (
|
<div className='emoji-mart-search'>
|
||||||
// It should not be transformed when mounting because the resulting
|
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
|
||||||
// size will be used to determine the coordinate of the menu by
|
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
|
||||||
// react-overlays
|
</div>
|
||||||
<div className={`language-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
|
||||||
<div className='emoji-mart-search'>
|
|
||||||
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
|
|
||||||
<button className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
|
<div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
|
||||||
{results.map(this.renderItem)}
|
{results.map(this.renderItem)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</Motion>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,14 +253,11 @@ class LanguageDropdown extends React.PureComponent {
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
};
|
};
|
||||||
|
|
||||||
handleToggle = ({ target }) => {
|
handleToggle = () => {
|
||||||
const { top } = target.getBoundingClientRect();
|
|
||||||
|
|
||||||
if (this.state.open && this.activeElement) {
|
if (this.state.open && this.activeElement) {
|
||||||
this.activeElement.focus({ preventScroll: true });
|
this.activeElement.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
|
|
||||||
this.setState({ open: !this.state.open });
|
this.setState({ open: !this.state.open });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,13 +277,25 @@ class LanguageDropdown extends React.PureComponent {
|
||||||
onChange(value);
|
onChange(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTargetRef = c => {
|
||||||
|
this.target = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
findTarget = () => {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOverlayEnter = (state) => {
|
||||||
|
this.setState({ placement: state.placement });
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { value, intl, frequentlyUsedLanguages } = this.props;
|
const { value, intl, frequentlyUsedLanguages } = this.props;
|
||||||
const { open, placement } = this.state;
|
const { open, placement } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('privacy-dropdown', { active: open })}>
|
<div className={classNames('privacy-dropdown', placement, { active: open })}>
|
||||||
<div className='privacy-dropdown__value'>
|
<div className='privacy-dropdown__value' ref={this.setTargetRef} >
|
||||||
<TextIconButton
|
<TextIconButton
|
||||||
className='privacy-dropdown__value-icon'
|
className='privacy-dropdown__value-icon'
|
||||||
label={value && value.toUpperCase()}
|
label={value && value.toUpperCase()}
|
||||||
|
@ -309,15 +305,20 @@ class LanguageDropdown extends React.PureComponent {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Overlay show={open} placement={placement} target={this}>
|
<Overlay show={open} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||||
<LanguageDropdownMenu
|
{({ props, placement }) => (
|
||||||
value={value}
|
<div {...props}>
|
||||||
frequentlyUsedLanguages={frequentlyUsedLanguages}
|
<div className={`dropdown-animation language-dropdown__dropdown ${placement}`} >
|
||||||
onClose={this.handleClose}
|
<LanguageDropdownMenu
|
||||||
onChange={this.handleChange}
|
value={value}
|
||||||
placement={placement}
|
frequentlyUsedLanguages={frequentlyUsedLanguages}
|
||||||
intl={intl}
|
onClose={this.handleClose}
|
||||||
/>
|
onChange={this.handleChange}
|
||||||
|
intl={intl}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,13 +3,12 @@ import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import spring from 'react-motion/lib/spring';
|
|
||||||
import {
|
import {
|
||||||
injectIntl,
|
injectIntl,
|
||||||
FormattedMessage,
|
FormattedMessage,
|
||||||
defineMessages,
|
defineMessages,
|
||||||
} from 'react-intl';
|
} from 'react-intl';
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
|
|
||||||
// Components.
|
// Components.
|
||||||
import Icon from 'flavours/glitch/components/icon';
|
import Icon from 'flavours/glitch/components/icon';
|
||||||
|
@ -17,7 +16,6 @@ import Icon from 'flavours/glitch/components/icon';
|
||||||
// Utils.
|
// Utils.
|
||||||
import { focusRoot } from 'flavours/glitch/utils/dom_helpers';
|
import { focusRoot } from 'flavours/glitch/utils/dom_helpers';
|
||||||
import { searchEnabled } from 'flavours/glitch/initial_state';
|
import { searchEnabled } from 'flavours/glitch/initial_state';
|
||||||
import Motion from '../../ui/util/optional_motion';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
|
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
|
||||||
|
@ -26,31 +24,20 @@ const messages = defineMessages({
|
||||||
|
|
||||||
class SearchPopout extends React.PureComponent {
|
class SearchPopout extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
style: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { style } = this.props;
|
|
||||||
const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
|
const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
|
||||||
return (
|
return (
|
||||||
<div style={{ ...style, position: 'absolute', width: 285, zIndex: 2 }}>
|
<div className='search-popout'>
|
||||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
<h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
|
||||||
{({ opacity, scaleX, scaleY }) => (
|
|
||||||
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
|
||||||
<h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
|
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
|
<li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
|
||||||
<li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
<li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
||||||
<li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
<li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
||||||
<li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
|
<li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{extraInformation}
|
{extraInformation}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Motion>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -136,6 +123,10 @@ class Search extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findTarget = () => {
|
||||||
|
return this.searchForm;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, value, submitted } = this.props;
|
const { intl, value, submitted } = this.props;
|
||||||
const { expanded } = this.state;
|
const { expanded } = this.state;
|
||||||
|
@ -161,8 +152,14 @@ class Search extends React.PureComponent {
|
||||||
<Icon id='search' className={hasValue ? '' : 'active'} />
|
<Icon id='search' className={hasValue ? '' : 'active'} />
|
||||||
<Icon id='times-circle' className={hasValue ? 'active' : ''} />
|
<Icon id='times-circle' className={hasValue ? 'active' : ''} />
|
||||||
</div>
|
</div>
|
||||||
<Overlay show={expanded && !hasValue} placement='bottom' target={this} container={this}>
|
<Overlay show={expanded && !hasValue} placement='bottom' target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||||
<SearchPopout />
|
{({ props, placement }) => (
|
||||||
|
<div {...props} style={{ ...props.style, width: 285, zIndex: 2 }}>
|
||||||
|
<div className={`dropdown-animation ${placement}`}>
|
||||||
|
<SearchPopout />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -43,13 +43,13 @@ export default class Upload extends ImmutablePureComponent {
|
||||||
{({ scale }) => (
|
{({ scale }) => (
|
||||||
<div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
|
<div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
|
||||||
<div className='compose-form__upload__actions'>
|
<div className='compose-form__upload__actions'>
|
||||||
<button className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
|
<button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
|
||||||
{!!media.get('unattached') && (<button className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>)}
|
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(media.get('description') || '').length === 0 && !!media.get('unattached') && (
|
{(media.get('description') || '').length === 0 && (
|
||||||
<div className='compose-form__upload__warning'>
|
<div className='compose-form__upload__warning'>
|
||||||
<button className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
|
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -46,7 +46,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||||
},
|
},
|
||||||
|
|
||||||
onDoodleOpen() {
|
onDoodleOpen() {
|
||||||
dispatch(openModal('DOODLE', { noEsc: true }));
|
dispatch(openModal('DOODLE', { noEsc: true, noClose: true }));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ColumnHeader from 'flavours/glitch/components/column_header';
|
||||||
|
import ScrollableList from 'flavours/glitch/components/scrollable_list';
|
||||||
|
import Column from 'flavours/glitch/features/ui/components/column';
|
||||||
|
import { Helmet } from 'react-helmet';
|
||||||
|
import Hashtag from 'flavours/glitch/components/hashtag';
|
||||||
|
import { expandFollowedHashtags, fetchFollowedHashtags } from 'flavours/glitch/actions/tags';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
heading: { id: 'followed_tags', defaultMessage: 'Followed hashtags' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
hashtags: state.getIn(['followed_tags', 'items']),
|
||||||
|
isLoading: state.getIn(['followed_tags', 'isLoading'], true),
|
||||||
|
hasMore: !!state.getIn(['followed_tags', 'next']),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
|
class FollowedTags extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
params: PropTypes.object.isRequired,
|
||||||
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
hashtags: ImmutablePropTypes.list,
|
||||||
|
isLoading: PropTypes.bool,
|
||||||
|
hasMore: PropTypes.bool,
|
||||||
|
multiColumn: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.dispatch(fetchFollowedHashtags());
|
||||||
|
};
|
||||||
|
|
||||||
|
handleLoadMore = debounce(() => {
|
||||||
|
this.props.dispatch(expandFollowedHashtags());
|
||||||
|
}, 300, { leading: true });
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { intl, hashtags, isLoading, hasMore, multiColumn } = this.props;
|
||||||
|
|
||||||
|
const emptyMessage = <FormattedMessage id='empty_column.followed_tags' defaultMessage='You have not followed any hashtags yet. When you do, they will show up here.' />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column bindToDocument={!multiColumn}>
|
||||||
|
<ColumnHeader
|
||||||
|
icon='hashtag'
|
||||||
|
title={intl.formatMessage(messages.heading)}
|
||||||
|
showBackButton
|
||||||
|
multiColumn={multiColumn}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ScrollableList
|
||||||
|
scrollKey='followed_tags'
|
||||||
|
emptyMessage={emptyMessage}
|
||||||
|
hasMore={hasMore}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onLoadMore={this.handleLoadMore}
|
||||||
|
bindToDocument={!multiColumn}
|
||||||
|
>
|
||||||
|
{hashtags.map((hashtag) => (
|
||||||
|
<Hashtag
|
||||||
|
key={hashtag.get('name')}
|
||||||
|
name={hashtag.get('name')}
|
||||||
|
to={`/tags/${hashtag.get('name')}`}
|
||||||
|
withGraph={false}
|
||||||
|
// Taken from ImmutableHashtag. Should maybe refactor ImmutableHashtag to accept more options?
|
||||||
|
people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1}
|
||||||
|
history={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ScrollableList>
|
||||||
|
|
||||||
|
<Helmet>
|
||||||
|
<meta name='robots' content='noindex' />
|
||||||
|
</Helmet>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,7 +13,6 @@ const messages = defineMessages({
|
||||||
general: { id: 'settings.general', defaultMessage: 'General' },
|
general: { id: 'settings.general', defaultMessage: 'General' },
|
||||||
compose: { id: 'settings.compose_box_opts', defaultMessage: 'Compose box' },
|
compose: { id: 'settings.compose_box_opts', defaultMessage: 'Compose box' },
|
||||||
content_warnings: { id: 'settings.content_warnings', defaultMessage: 'Content Warnings' },
|
content_warnings: { id: 'settings.content_warnings', defaultMessage: 'Content Warnings' },
|
||||||
filters: { id: 'settings.filters', defaultMessage: 'Filters' },
|
|
||||||
collapsed: { id: 'settings.collapsed_statuses', defaultMessage: 'Collapsed toots' },
|
collapsed: { id: 'settings.collapsed_statuses', defaultMessage: 'Collapsed toots' },
|
||||||
media: { id: 'settings.media', defaultMessage: 'Media' },
|
media: { id: 'settings.media', defaultMessage: 'Media' },
|
||||||
preferences: { id: 'settings.preferences', defaultMessage: 'Preferences' },
|
preferences: { id: 'settings.preferences', defaultMessage: 'Preferences' },
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { me, maxReactions } from 'flavours/glitch/initial_state';
|
import { me, maxReactions } from 'flavours/glitch/initial_state';
|
||||||
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions';
|
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||||
import EmojiPickerDropdown from '../../compose/containers/emoji_picker_dropdown_container';
|
import EmojiPickerDropdown from '../../compose/containers/emoji_picker_dropdown_container';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -36,6 +36,7 @@ const messages = defineMessages({
|
||||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
||||||
|
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
||||||
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
||||||
});
|
});
|
||||||
|
@ -186,19 +187,19 @@ class ActionBar extends React.PureComponent {
|
||||||
menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
|
menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
|
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
||||||
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) {
|
if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
if (accountAdminLink !== undefined) {
|
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||||
menu.push({
|
if (accountAdminLink !== undefined) {
|
||||||
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) });
|
||||||
href: accountAdminLink(status.getIn(['account', 'id'])),
|
}
|
||||||
});
|
if (statusAdminLink !== undefined) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (statusAdminLink !== undefined) {
|
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
||||||
menu.push({
|
const domain = status.getIn(['account', 'acct']).split('@')[1];
|
||||||
text: intl.formatMessage(messages.admin_status),
|
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
|
||||||
href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,10 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
compact: PropTypes.bool,
|
compact: PropTypes.bool,
|
||||||
showMedia: PropTypes.bool,
|
showMedia: PropTypes.bool,
|
||||||
usingPiP: PropTypes.bool,
|
pictureInPicture: ImmutablePropTypes.contains({
|
||||||
|
inUse: PropTypes.bool,
|
||||||
|
available: PropTypes.bool,
|
||||||
|
}),
|
||||||
onToggleMediaVisibility: PropTypes.func,
|
onToggleMediaVisibility: PropTypes.func,
|
||||||
onReactionAdd: PropTypes.func.isRequired,
|
onReactionAdd: PropTypes.func.isRequired,
|
||||||
onReactionRemove: PropTypes.func.isRequired,
|
onReactionRemove: PropTypes.func.isRequired,
|
||||||
|
@ -124,7 +127,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
||||||
const { expanded, onToggleHidden, settings, usingPiP, intl } = this.props;
|
const { expanded, onToggleHidden, settings, pictureInPicture, intl } = this.props;
|
||||||
const outerStyle = { boxSizing: 'border-box' };
|
const outerStyle = { boxSizing: 'border-box' };
|
||||||
const { compact } = this.props;
|
const { compact } = this.props;
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
outerStyle.height = `${this.state.height}px`;
|
outerStyle.height = `${this.state.height}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usingPiP) {
|
if (pictureInPicture.get('inUse')) {
|
||||||
media.push(<PictureInPicturePlaceholder />);
|
media.push(<PictureInPicturePlaceholder />);
|
||||||
mediaIcons.push('video-camera');
|
mediaIcons.push('video-camera');
|
||||||
} else if (status.get('media_attachments').size > 0) {
|
} else if (status.get('media_attachments').size > 0) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ import { initMuteModal } from 'flavours/glitch/actions/mutes';
|
||||||
import { initBlockModal } from 'flavours/glitch/actions/blocks';
|
import { initBlockModal } from 'flavours/glitch/actions/blocks';
|
||||||
import { initReport } from 'flavours/glitch/actions/reports';
|
import { initReport } from 'flavours/glitch/actions/reports';
|
||||||
import { initBoostModal } from 'flavours/glitch/actions/boosts';
|
import { initBoostModal } from 'flavours/glitch/actions/boosts';
|
||||||
import { makeGetStatus } from 'flavours/glitch/selectors';
|
import { makeCustomEmojiMap, makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors';
|
||||||
import ScrollContainer from 'flavours/glitch/containers/scroll_container';
|
import ScrollContainer from 'flavours/glitch/containers/scroll_container';
|
||||||
import ColumnBackButton from 'flavours/glitch/components/column_back_button';
|
import ColumnBackButton from 'flavours/glitch/components/column_back_button';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
@ -69,11 +69,12 @@ const messages = defineMessages({
|
||||||
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
||||||
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?' },
|
||||||
tootHeading: { id: 'column.toot', defaultMessage: 'Toots and replies' },
|
tootHeading: { id: 'account.posts_with_replies', defaultMessage: 'Posts and replies' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const makeMapStateToProps = () => {
|
const makeMapStateToProps = () => {
|
||||||
const getStatus = makeGetStatus();
|
const getStatus = makeGetStatus();
|
||||||
|
const getPictureInPicture = makeGetPictureInPicture();
|
||||||
|
|
||||||
const getAncestorsIds = createSelector([
|
const getAncestorsIds = createSelector([
|
||||||
(_, { id }) => id,
|
(_, { id }) => id,
|
||||||
|
@ -131,11 +132,12 @@ const makeMapStateToProps = () => {
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const status = getStatus(state, { id: props.params.statusId });
|
const status = getStatus(state, { id: props.params.statusId });
|
||||||
let ancestorsIds = Immutable.List();
|
|
||||||
|
let ancestorsIds = Immutable.List();
|
||||||
let descendantsIds = Immutable.List();
|
let descendantsIds = Immutable.List();
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
ancestorsIds = getAncestorsIds(state, { id: status.get('in_reply_to_id') });
|
ancestorsIds = getAncestorsIds(state, { id: status.get('in_reply_to_id') });
|
||||||
descendantsIds = getDescendantsIds(state, { id: status.get('id') });
|
descendantsIds = getDescendantsIds(state, { id: status.get('id') });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +149,7 @@ const makeMapStateToProps = () => {
|
||||||
settings: state.get('local_settings'),
|
settings: state.get('local_settings'),
|
||||||
askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0,
|
askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0,
|
||||||
domain: state.getIn(['meta', 'domain']),
|
domain: state.getIn(['meta', 'domain']),
|
||||||
usingPiP: state.get('picture_in_picture').statusId === props.params.statusId,
|
pictureInPicture: getPictureInPicture(state, { id: props.params.statusId }),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -192,7 +194,10 @@ class Status extends ImmutablePureComponent {
|
||||||
askReplyConfirmation: PropTypes.bool,
|
askReplyConfirmation: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
usingPiP: PropTypes.bool,
|
pictureInPicture: ImmutablePropTypes.contains({
|
||||||
|
inUse: PropTypes.bool,
|
||||||
|
available: PropTypes.bool,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -619,7 +624,7 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let ancestors, descendants;
|
let ancestors, descendants;
|
||||||
const { isLoading, status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, usingPiP } = this.props;
|
const { isLoading, status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
|
||||||
const { fullscreen } = this.state;
|
const { fullscreen } = this.state;
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
@ -699,7 +704,7 @@ class Status extends ImmutablePureComponent {
|
||||||
domain={domain}
|
domain={domain}
|
||||||
showMedia={this.state.showMedia}
|
showMedia={this.state.showMedia}
|
||||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||||
usingPiP={usingPiP}
|
pictureInPicture={pictureInPicture}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionBar
|
<ActionBar
|
||||||
|
|
|
@ -320,7 +320,7 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<label className='setting-text-label' htmlFor='upload-modal__thumbnail'><FormattedMessage id='upload_form.thumbnail' defaultMessage='Change thumbnail' /></label>
|
<label className='setting-text-label' htmlFor='upload-modal__thumbnail'><FormattedMessage id='upload_form.thumbnail' defaultMessage='Change thumbnail' /></label>
|
||||||
|
|
||||||
<Button disabled={isUploadingThumbnail} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
|
<Button disabled={isUploadingThumbnail || !media.get('unattached')} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.chooseImage)}</span>
|
<span style={{ display: 'none' }}>{intl.formatMessage(messages.chooseImage)}</span>
|
||||||
|
|
|
@ -52,6 +52,8 @@ class LinkFooter extends React.PureComponent {
|
||||||
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
|
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
|
||||||
const canProfileDirectory = profileDirectory;
|
const canProfileDirectory = profileDirectory;
|
||||||
|
|
||||||
|
const DividingCircle = <span aria-hidden>{' · '}</span>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='link-footer'>
|
<div className='link-footer'>
|
||||||
<p>
|
<p>
|
||||||
|
@ -60,17 +62,17 @@ class LinkFooter extends React.PureComponent {
|
||||||
<Link key='about' to='/about'><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
|
<Link key='about' to='/about'><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
|
||||||
{canInvite && (
|
{canInvite && (
|
||||||
<>
|
<>
|
||||||
{' · '}
|
{DividingCircle}
|
||||||
<a key='invites' href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
|
<a key='invites' href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{canProfileDirectory && (
|
{canProfileDirectory && (
|
||||||
<>
|
<>
|
||||||
{' · '}
|
{DividingCircle}
|
||||||
<Link key='directory' to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
|
<Link key='directory' to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{' · '}
|
{DividingCircle}
|
||||||
<Link key='privacy-policy' to='/privacy-policy'><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
|
<Link key='privacy-policy' to='/privacy-policy'><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -78,13 +80,13 @@ class LinkFooter extends React.PureComponent {
|
||||||
<strong>Mastodon, queer.af edition</strong>:
|
<strong>Mastodon, queer.af edition</strong>:
|
||||||
{' '}
|
{' '}
|
||||||
<a href='https://joinmastodon.org' target='_blank'><FormattedMessage id='footer.about' defaultMessage='About' /></a>
|
<a href='https://joinmastodon.org' target='_blank'><FormattedMessage id='footer.about' defaultMessage='About' /></a>
|
||||||
{' · '}
|
{DividingCircle}
|
||||||
<a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='footer.get_app' defaultMessage='Get the app' /></a>
|
<a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='footer.get_app' defaultMessage='Get the app' /></a>
|
||||||
{' · '}
|
{DividingCircle}
|
||||||
<Link to='/keyboard-shortcuts'><FormattedMessage id='footer.keyboard_shortcuts' defaultMessage='Keyboard shortcuts' /></Link>
|
<Link to='/keyboard-shortcuts'><FormattedMessage id='footer.keyboard_shortcuts' defaultMessage='Keyboard shortcuts' /></Link>
|
||||||
{' · '}
|
{DividingCircle}
|
||||||
<a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
|
<a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
|
||||||
{' · '}
|
{DividingCircle}
|
||||||
v{version}
|
v{version}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -116,13 +116,16 @@ export default class ModalRoot extends React.PureComponent {
|
||||||
this._modal = c;
|
this._modal = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prevent closing of modal when clicking the overlay
|
||||||
|
noop = () => {}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { type, props, ignoreFocus } = this.props;
|
const { type, props, ignoreFocus } = this.props;
|
||||||
const { backgroundColor } = this.state;
|
const { backgroundColor } = this.state;
|
||||||
const visible = !!type;
|
const visible = !!type;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Base backgroundColor={backgroundColor} onClose={this.handleClose} noEsc={props ? props.noEsc : false} ignoreFocus={ignoreFocus}>
|
<Base backgroundColor={backgroundColor} onClose={props && props.noClose ? this.noop : this.handleClose} noEsc={props ? props.noEsc : false} ignoreFocus={ignoreFocus}>
|
||||||
{visible && (
|
{visible && (
|
||||||
<>
|
<>
|
||||||
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
|
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
|
||||||
|
|
|
@ -30,7 +30,7 @@ const SignInBanner = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='sign-in-banner'>
|
<div className='sign-in-banner'>
|
||||||
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.' /></p>
|
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.' /></p>
|
||||||
<a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
|
<a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
|
||||||
{signupButton}
|
{signupButton}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50,6 +50,7 @@ export default class VideoModal extends ImmutablePureComponent {
|
||||||
autoPlay={options.autoPlay}
|
autoPlay={options.autoPlay}
|
||||||
volume={options.defaultVolume}
|
volume={options.defaultVolume}
|
||||||
onCloseVideo={onClose}
|
onCloseVideo={onClose}
|
||||||
|
autoFocus
|
||||||
detailed
|
detailed
|
||||||
alt={media.get('description')}
|
alt={media.get('description')}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -42,6 +42,7 @@ import {
|
||||||
FollowRequests,
|
FollowRequests,
|
||||||
FavouritedStatuses,
|
FavouritedStatuses,
|
||||||
BookmarkedStatuses,
|
BookmarkedStatuses,
|
||||||
|
FollowedTags,
|
||||||
ListTimeline,
|
ListTimeline,
|
||||||
Blocks,
|
Blocks,
|
||||||
DomainBlocks,
|
DomainBlocks,
|
||||||
|
@ -56,7 +57,7 @@ import {
|
||||||
PrivacyPolicy,
|
PrivacyPolicy,
|
||||||
} from './util/async-components';
|
} from './util/async-components';
|
||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
import initialState, { me, owner, singleUserMode, showTrends } from '../../initial_state';
|
import initialState, { me, owner, singleUserMode, showTrends, trendsAsLanding } from '../../initial_state';
|
||||||
import { closeOnboarding, INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding';
|
import { closeOnboarding, INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding';
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
@ -177,7 +178,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
}
|
}
|
||||||
} else if (singleUserMode && owner && initialState?.accounts[owner]) {
|
} else if (singleUserMode && owner && initialState?.accounts[owner]) {
|
||||||
redirect = <Redirect from='/' to={`/@${initialState.accounts[owner].username}`} exact />;
|
redirect = <Redirect from='/' to={`/@${initialState.accounts[owner].username}`} exact />;
|
||||||
} else if (showTrends) {
|
} else if (showTrends && trendsAsLanding) {
|
||||||
redirect = <Redirect from='/' to='/explore' exact />;
|
redirect = <Redirect from='/' to='/explore' exact />;
|
||||||
} else {
|
} else {
|
||||||
redirect = <Redirect from='/' to='/about' exact />;
|
redirect = <Redirect from='/' to='/about' exact />;
|
||||||
|
@ -230,6 +231,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||||
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
||||||
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} />
|
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} />
|
||||||
|
<WrappedRoute path='/followed_tags' component={FollowedTags} content={children} />
|
||||||
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
||||||
<WrappedRoute path='/lists' component={Lists} content={children} />
|
<WrappedRoute path='/lists' component={Lists} content={children} />
|
||||||
<WrappedRoute path='/getting-started-misc' component={GettingStartedMisc} content={children} />
|
<WrappedRoute path='/getting-started-misc' component={GettingStartedMisc} content={children} />
|
||||||
|
|
|
@ -98,6 +98,10 @@ export function FavouritedStatuses () {
|
||||||
return import(/* webpackChunkName: "flavours/glitch/async/favourited_statuses" */'flavours/glitch/features/favourited_statuses');
|
return import(/* webpackChunkName: "flavours/glitch/async/favourited_statuses" */'flavours/glitch/features/favourited_statuses');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FollowedTags () {
|
||||||
|
return import(/* webpackChunkName: "flavours/glitch/async/followed_tags" */'flavours/glitch/features/followed_tags');
|
||||||
|
}
|
||||||
|
|
||||||
export function BookmarkedStatuses () {
|
export function BookmarkedStatuses () {
|
||||||
return import(/* webpackChunkName: "flavours/glitch/async/bookmarked_statuses" */'flavours/glitch/features/bookmarked_statuses');
|
return import(/* webpackChunkName: "flavours/glitch/async/bookmarked_statuses" */'flavours/glitch/features/bookmarked_statuses');
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,7 @@ class Video extends React.PureComponent {
|
||||||
volume: PropTypes.number,
|
volume: PropTypes.number,
|
||||||
muted: PropTypes.bool,
|
muted: PropTypes.bool,
|
||||||
componentIndex: PropTypes.number,
|
componentIndex: PropTypes.number,
|
||||||
|
autoFocus: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -537,7 +538,7 @@ class Video extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth, detailed, sensitive, editable, blurhash } = this.props;
|
const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
|
||||||
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
const { containerWidth, 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 = {};
|
const playerStyle = {};
|
||||||
|
@ -635,7 +636,7 @@ class Video extends React.PureComponent {
|
||||||
|
|
||||||
<div className='video-player__buttons-bar'>
|
<div className='video-player__buttons-bar'>
|
||||||
<div className='video-player__buttons left'>
|
<div className='video-player__buttons left'>
|
||||||
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={detailed}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
|
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={autoFocus}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
|
||||||
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
|
<button type='button' title={intl.formatMessage(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={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
<div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
* @property {boolean} timeline_preview
|
* @property {boolean} timeline_preview
|
||||||
* @property {string} title
|
* @property {string} title
|
||||||
* @property {boolean} trends
|
* @property {boolean} trends
|
||||||
|
* @property {boolean} trends_as_landing_page
|
||||||
* @property {boolean} unfollow_modal
|
* @property {boolean} unfollow_modal
|
||||||
* @property {boolean} use_blurhash
|
* @property {boolean} use_blurhash
|
||||||
* @property {boolean=} use_pending_items
|
* @property {boolean=} use_pending_items
|
||||||
|
@ -137,6 +138,7 @@ export const singleUserMode = getMeta('single_user_mode');
|
||||||
export const source_url = getMeta('source_url');
|
export const source_url = getMeta('source_url');
|
||||||
export const timelinePreview = getMeta('timeline_preview');
|
export const timelinePreview = getMeta('timeline_preview');
|
||||||
export const title = getMeta('title');
|
export const title = getMeta('title');
|
||||||
|
export const trendsAsLanding = getMeta('trends_as_landing_page');
|
||||||
export const unfollowModal = getMeta('unfollow_modal');
|
export const unfollowModal = getMeta('unfollow_modal');
|
||||||
export const useBlurhash = getMeta('use_blurhash');
|
export const useBlurhash = getMeta('use_blurhash');
|
||||||
export const usePendingItems = getMeta('use_pending_items');
|
export const usePendingItems = getMeta('use_pending_items');
|
||||||
|
|
|
@ -23,15 +23,14 @@ function loadPolyfills() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Latest version of Firefox and Safari do not have IntersectionObserver.
|
// Latest version of Firefox and Safari do not have IntersectionObserver.
|
||||||
// Edge does not have requestIdleCallback and object-fit CSS property.
|
// Edge does not have requestIdleCallback.
|
||||||
// This avoids shipping them all the polyfills.
|
// This avoids shipping them all the polyfills.
|
||||||
const needsExtraPolyfills = !(
|
const needsExtraPolyfills = !(
|
||||||
window.AbortController &&
|
window.AbortController &&
|
||||||
window.IntersectionObserver &&
|
window.IntersectionObserver &&
|
||||||
window.IntersectionObserverEntry &&
|
window.IntersectionObserverEntry &&
|
||||||
'isIntersecting' in IntersectionObserverEntry.prototype &&
|
'isIntersecting' in IntersectionObserverEntry.prototype &&
|
||||||
window.requestIdleCallback &&
|
window.requestIdleCallback
|
||||||
'object-fit' in (new Image()).style
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/af.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
1
app/javascript/flavours/glitch/locales/af.json
Normal file
1
app/javascript/flavours/glitch/locales/af.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
app/javascript/flavours/glitch/locales/an.json
Normal file
1
app/javascript/flavours/glitch/locales/an.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/ar.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/ar.json
Normal file
6
app/javascript/flavours/glitch/locales/ar.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/ast.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/ast.json
Normal file
6
app/javascript/flavours/glitch/locales/ast.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
1
app/javascript/flavours/glitch/locales/be.json
Normal file
1
app/javascript/flavours/glitch/locales/be.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/bg.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/bg.json
Normal file
6
app/javascript/flavours/glitch/locales/bg.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/bn.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/bn.json
Normal file
6
app/javascript/flavours/glitch/locales/bn.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/br.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/br.json
Normal file
6
app/javascript/flavours/glitch/locales/br.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
1
app/javascript/flavours/glitch/locales/bs.json
Normal file
1
app/javascript/flavours/glitch/locales/bs.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/ca.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/ca.json
Normal file
6
app/javascript/flavours/glitch/locales/ca.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/ckb.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/ckb.json
Normal file
6
app/javascript/flavours/glitch/locales/ckb.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/co.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/co.json
Normal file
6
app/javascript/flavours/glitch/locales/co.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
|
@ -1,180 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/cs.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
'about.fork_disclaimer': 'Glitch-soc je svobodný software s otevřeným zdrojovým kódem založený na Mastodonu.',
|
|
||||||
'settings.layout_opts': 'Možnosti rozvržení',
|
|
||||||
'settings.layout': 'Rozložení:',
|
|
||||||
'layout.current_is': 'Nastavené rozložení je:',
|
|
||||||
'layout.auto': 'Automatické',
|
|
||||||
'layout.desktop': 'Desktop',
|
|
||||||
'layout.mobile': 'Mobil',
|
|
||||||
'layout.hint.auto': 'Vybrat rozložení automaticky v závislosti na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.',
|
|
||||||
'layout.hint.desktop': 'Použít vícesloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.',
|
|
||||||
'layout.hint.single': 'Použít jednosloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.',
|
|
||||||
'navigation_bar.app_settings': 'Nastavení aplikace',
|
|
||||||
'navigation_bar.featured_users': 'Vybraní uživatelé',
|
|
||||||
'endorsed_accounts_editor.endorsed_accounts': 'Vybrané účty',
|
|
||||||
'navigation_bar.info': 'Rozšířené informace',
|
|
||||||
'navigation_bar.misc': 'Různé',
|
|
||||||
'navigation_bar.keyboard_shortcuts': 'Klávesové zkratky',
|
|
||||||
'getting_started.onboarding': 'Ukaž mi to tu',
|
|
||||||
'onboarding.skip': 'Přeskočit',
|
|
||||||
'onboarding.next': 'Další',
|
|
||||||
'onboarding.done': 'Hotovo',
|
|
||||||
'onboarding.page_one.federation': '{domain} je \'instance\' Mastodonu. Mastodon je síť nezávislých serverů, které jsou spolu propojené do jedné velké sociální sítě. Těmto serverům říkáme instance.',
|
|
||||||
'onboarding.page_one.handle': 'Jste na instanci {domain}, takže celá adresa vašeho profilu je {handle}',
|
|
||||||
'onboarding.page_one.welcome': 'Vítá vás {domain}!',
|
|
||||||
'onboarding.page_two.compose': 'Příspěvky se píší v levém sloupci. Pomocí ikon pod příspěvkem k němu můžete připojit obrázky, změnit úroveň soukromí nebo přidat varování o obsahu.',
|
|
||||||
'onboarding.page_three.search': 'Pomocí vyhledávací lišty můžete hledat lidi nebo hashtagy. Pokud hledáte někoho z jiné instance, musíte použít celou adresu jeho profilu.',
|
|
||||||
'onboarding.page_three.profile': 'Upravte si svůj profil a nastavte si profilový obrázek, jméno, a krátký text o sobě. Naleznete tam i další možnosti nastavení.',
|
|
||||||
'onboarding.page_four.home': 'Domovská časová osa zobrazuje příspěvky od lidí, které sledujete.',
|
|
||||||
'onboarding.page_four.notifications': 'Notifikace se zobrazí, když s vámi někdo interaguje.',
|
|
||||||
'onboarding.page_five.public_timelines': 'Místní časová osa zobrazuje veřejné příspěvky všech uživatelů instance {domain}. Federovaná časová osa zobrazí příspěvky od všech, koho uživatelé instance {domain} sledují. Tyto veřejné časové osy jsou skvělý způsob, jak objevit nové lidi.',
|
|
||||||
'onboarding.page_six.almost_done': 'Skoro hotovo...',
|
|
||||||
'onboarding.page_six.github': 'Na serveru {domain} běží Glitchsoc. Glitchsoc je přátelský {fork} programu {Mastodon}, a je kompatibilní s jakoukoliv jinou mastodoní instancí nebo aplikací. Glitchsoc je zcela svobodný a má otevřený zdrojový kód. Na stránce {github} můžete hlásit chyby, žádat o nové funkce, nebo ke kódu vlastnoručně přispět.',
|
|
||||||
'onboarding.page_six.apps_available': 'Jsou dostupné {apps} pro iOS, Android i jiné platformy.',
|
|
||||||
'onboarding.page_six.various_app': 'mobilní aplikace',
|
|
||||||
'onboarding.page_six.appetoot': 'Veselé mastodonění!',
|
|
||||||
'settings.auto_collapse': 'Automaticky sbalit',
|
|
||||||
'settings.auto_collapse_all': 'Všechno',
|
|
||||||
'settings.auto_collapse_lengthy': 'Dlouhé příspěvky',
|
|
||||||
'settings.auto_collapse_media': 'Příspěvky s přílohami',
|
|
||||||
'settings.auto_collapse_notifications': 'Oznámení',
|
|
||||||
'settings.auto_collapse_reblogs': 'Boosty',
|
|
||||||
'settings.auto_collapse_replies': 'Odpovědi',
|
|
||||||
'settings.show_action_bar': 'Zobrazit ve sbalených příspěvcích tlačítka s akcemi',
|
|
||||||
'settings.close': 'Zavřít',
|
|
||||||
'settings.collapsed_statuses': 'Sbalené příspěvky',
|
|
||||||
'settings.confirm_boost_missing_media_description': 'Zobrazit potvrzovací dialog před boostnutím příspěvku s chybějícími popisky obrázků',
|
|
||||||
'boost_modal.missing_description': 'Příspěvek obsahuje obrázky bez popisků',
|
|
||||||
'settings.enable_collapsed': 'Povolit sbalené příspěvky',
|
|
||||||
'settings.enable_collapsed_hint': 'U sbalených příspěvků je část jejich obsahu skrytá, aby zabraly méně místa na obrazovce. (Tohle není stejná funkce jako varování o obsahu.)',
|
|
||||||
'settings.general': 'Obecné',
|
|
||||||
'settings.hicolor_privacy_icons': 'Barevné ikony soukromí',
|
|
||||||
'settings.hicolor_privacy_icons.hint': 'Zobrazit ikony úrovně soukromí příspěvků v jasných, snadno rozlišitelných barvách',
|
|
||||||
'settings.image_backgrounds': 'Obrázkové pozadí',
|
|
||||||
'settings.image_backgrounds_media': 'Náhled médií ve sbalených příspěvcích',
|
|
||||||
'settings.image_backgrounds_media_hint': 'Pokud jsou k příspěvku přiložena média, použije se první z nich jako pozadí',
|
|
||||||
'settings.image_backgrounds_users': 'Nastavit sbaleným příspěvkům obrázkové pozadí',
|
|
||||||
'settings.inline_preview_cards': 'Zobrazit v časové ose náhledy externích odkazů',
|
|
||||||
'settings.media': 'Média',
|
|
||||||
'settings.media_letterbox': 'Neořezávat obrázky',
|
|
||||||
'settings.media_letterbox_hint': 'Místo výřezu obrázku zobrazit obrázek celý, doplněný podle potřeby o prázdné okraje',
|
|
||||||
'settings.media_fullwidth': 'Zobrazit náhledy v plné šířce',
|
|
||||||
'settings.notifications_opts': 'Možnosti oznámení',
|
|
||||||
'settings.notifications.tab_badge': 'Zobrazit počet nepřečtených oznámení',
|
|
||||||
'settings.notifications.tab_badge.hint': 'Počet nepřečtených oznámení se viditelně zobrazí na hlavní stránce (pokud není seznam oznámení viditelný)',
|
|
||||||
'settings.notifications.favicon_badge': 'Zobrazit počet na ikoně serveru',
|
|
||||||
'settings.notifications.favicon_badge.hint': 'Zobrazí počet nepřečtených oznámení na ikoně serveru',
|
|
||||||
'settings.preferences': 'Předvolby',
|
|
||||||
'settings.rewrite_mentions': 'Přepsat zmínky v zobrazených příspěvcích',
|
|
||||||
'settings.rewrite_mentions_no': 'Nepřepisovat zmínky',
|
|
||||||
'settings.rewrite_mentions_acct': 'Přepsat uživatelským jménem a doménou (pokud je účet na jiném serveru)',
|
|
||||||
'settings.rewrite_mentions_username': 'Přepsat uživatelským jménem',
|
|
||||||
'settings.show_reply_counter': 'Zobrazit odhad počtu odpovědí',
|
|
||||||
'settings.status_icons': 'Ikony u příspěvků',
|
|
||||||
'settings.status_icons_language': 'Indikace jazyk',
|
|
||||||
'settings.status_icons_reply': 'Indikace odpovědi',
|
|
||||||
'settings.status_icons_local_only': 'Indikace lokálního příspěvku',
|
|
||||||
'settings.status_icons_media': 'Indikace obrázků a anket',
|
|
||||||
'settings.status_icons_visibility': 'Indikace úrovně soukromí',
|
|
||||||
'settings.tag_misleading_links': 'Označit zavádějící odkazy',
|
|
||||||
'settings.tag_misleading_links.hint': 'Zobrazit skutečný cíl u každého odkazu, který ho explicitně nezmiňuje',
|
|
||||||
'settings.wide_view': 'Široké sloupce (pouze v režimu Desktop)',
|
|
||||||
'settings.wide_view_hint': 'Sloupce se roztáhnout, aby lépe vyplnily dostupný prostor.',
|
|
||||||
'settings.navbar_under': 'Navigační lišta vespod (pouze v režimu Mobil)',
|
|
||||||
'settings.compose_box_opts': 'Editační pole',
|
|
||||||
'settings.always_show_spoilers_field': 'Vždy zobrazit pole pro varování o obsahu',
|
|
||||||
'settings.prepend_cw_re': 'Při odpovídání přidat před varování o obsahu “re: ”',
|
|
||||||
'settings.preselect_on_reply': 'Při odpovědi označit uživatelská jména',
|
|
||||||
'settings.preselect_on_reply_hint': 'Při odpovídání na konverzaci s více účastníky se jména všech kromě prvního označí, aby šla jednoduše smazat',
|
|
||||||
'settings.confirm_missing_media_description': 'Zobrazit potvrzovací dialog při odesílání příspěvku, ve kterém chybí popisky obrázků',
|
|
||||||
'settings.confirm_before_clearing_draft': 'Zobrazit potvrzovací dialog před přepsáním právě vytvářené zprávy',
|
|
||||||
'settings.show_content_type_choice': 'Zobrazit volbu formátu příspěvku',
|
|
||||||
'settings.side_arm': 'Vedlejší odesílací tlačítko:',
|
|
||||||
'settings.side_arm.none': 'Žádné',
|
|
||||||
'settings.side_arm_reply_mode': 'Při odpovídání na příspěvek by vedlejší odesílací tlačítko mělo:',
|
|
||||||
'settings.side_arm_reply_mode.keep': 'Použít svou nastavenou úroveň soukromí',
|
|
||||||
'settings.side_arm_reply_mode.copy': 'Použít úroveň soukromí příspěvku, na který odpovídáte',
|
|
||||||
'settings.side_arm_reply_mode.restrict': 'Zvýšit úroveň soukromí nejméně na úroveň příspěvku, na který odpovídáte',
|
|
||||||
'settings.content_warnings': 'Varování o obsahu',
|
|
||||||
'settings.content_warnings_shared_state': 'Zobrazit/schovat všechny kopie naráz',
|
|
||||||
'settings.content_warnings_shared_state_hint': 'Tlačítko varování o obsahu bude mít efekt na všechny kopie příspěvku naráz, stejně jako na běžném Mastodonu. Nebude pak možné automaticky sbalit jakoukoliv kopii příspěvku, která má rozbalené varování o obsahu',
|
|
||||||
'settings.content_warnings_media_outside': 'Zobrazit obrázky a videa mimo varování o obsahu',
|
|
||||||
'settings.content_warnings_media_outside_hint': 'Obrázky a videa z příspěvku s varováním o obsahu se zobrazí se separátním přepínačem zobrazení, stejně jako na běžném Mastodonu.',
|
|
||||||
'settings.content_warnings_unfold_opts': 'Možnosti automatického rozbalení',
|
|
||||||
'settings.enable_content_warnings_auto_unfold': 'Vždy rozbalit příspěvky označené varováním o obsahu',
|
|
||||||
'settings.deprecated_setting': 'Tato možnost se nyní nastavuje v {settings_page_link}',
|
|
||||||
'settings.shared_settings_link': 'předvolbách Mastodonu',
|
|
||||||
'settings.content_warnings_filter': 'Tato varování o obsahu automaticky nerozbalovat:',
|
|
||||||
'settings.content_warnings.regexp': 'Regulární výraz',
|
|
||||||
'settings.media_reveal_behind_cw': 'Automaticky zobrazit média označená varováním o obsahu',
|
|
||||||
'settings.pop_in_player': 'Povolit plovoucí okno přehrávače',
|
|
||||||
'settings.pop_in_position': 'Pozice plovoucího okna:',
|
|
||||||
'settings.pop_in_left': 'Vlevo',
|
|
||||||
'settings.pop_in_right': 'Vpravo',
|
|
||||||
|
|
||||||
|
|
||||||
'status.collapse': 'Sbalit',
|
|
||||||
'status.uncollapse': 'Rozbalit',
|
|
||||||
'status.in_reply_to': 'Tento příspěvek je odpověď',
|
|
||||||
'status.has_preview_card': 'Obsahuje náhled odkazu',
|
|
||||||
'status.has_pictures': 'Obsahuje obrázky',
|
|
||||||
'status.is_poll': 'Tento příspěvek je anketa',
|
|
||||||
'status.has_video': 'Obsahuje video',
|
|
||||||
'status.has_audio': 'Obsahuje audio',
|
|
||||||
'status.local_only': 'Viditelné pouze z vaší instance',
|
|
||||||
|
|
||||||
'media_gallery.sensitive': 'Citlivý obsah',
|
|
||||||
|
|
||||||
'favourite_modal.combo': 'Příště můžete pro přeskočení stisknout {combo}',
|
|
||||||
|
|
||||||
'home.column_settings.show_direct': 'Zobrazit přímé zprávy',
|
|
||||||
|
|
||||||
'notification_purge.start': 'Čistící režim',
|
|
||||||
'notifications.mark_as_read': 'Označit všechna oznámení jako přečtená',
|
|
||||||
|
|
||||||
'notification.markForDeletion': 'Označit pro smazání',
|
|
||||||
'notifications.clear': 'Vymazat všechna oznámení',
|
|
||||||
'notifications.marked_clear_confirmation': 'Určitě chcete trvale smazat všechna vybraná oznámení?',
|
|
||||||
'notifications.marked_clear': 'Smazat vybraná oznámení',
|
|
||||||
|
|
||||||
'notification_purge.btn_all': 'Vybrat\nvše',
|
|
||||||
'notification_purge.btn_none': 'Nevybrat\nnic',
|
|
||||||
'notification_purge.btn_invert': 'Obrátit\nvýběr',
|
|
||||||
'notification_purge.btn_apply': 'Smazat\nvybrané',
|
|
||||||
|
|
||||||
'compose.attach.upload': 'Nahrát soubor',
|
|
||||||
'compose.attach.doodle': 'Něco namalovat',
|
|
||||||
'compose.attach': 'Připojit...',
|
|
||||||
|
|
||||||
'advanced_options.local-only.short': 'Lokální příspěvek',
|
|
||||||
'advanced_options.local-only.long': 'Neposílat na jiné servery',
|
|
||||||
'advanced_options.local-only.tooltip': 'Tento příspěvek je pouze lokální',
|
|
||||||
'advanced_options.icon_title': 'Pokročilá nastavení',
|
|
||||||
'advanced_options.threaded_mode.short': 'Režim vlákna',
|
|
||||||
'advanced_options.threaded_mode.long': 'Po odeslání automaticky otevře pole pro odpověď',
|
|
||||||
'advanced_options.threaded_mode.tooltip': 'Režim vlákna je zapnutý',
|
|
||||||
|
|
||||||
'home.column_settings.advanced': 'Pokročilé',
|
|
||||||
'home.column_settings.filter_regex': 'Filtrovat podle regulárních výrazů',
|
|
||||||
|
|
||||||
'compose_form.poll.single_choice': 'Povolit jednu odpověď',
|
|
||||||
'compose_form.poll.multiple_choices': 'Povolit více odpovědí',
|
|
||||||
|
|
||||||
'compose.content-type.plain': 'Prostý text',
|
|
||||||
'content-type.change': 'Formát příspěvku',
|
|
||||||
'compose_form.spoiler': 'Přidat varování o obsahu',
|
|
||||||
|
|
||||||
'direct.group_by_conversations': 'Seskupit do konverzací',
|
|
||||||
'column.toot': 'Příspěvky a odpovědi',
|
|
||||||
'confirmation_modal.do_not_ask_again': 'Příště se už neptat',
|
|
||||||
|
|
||||||
'keyboard_shortcuts.bookmark': 'Přidat do záložek',
|
|
||||||
'keyboard_shortcuts.toggle_collapse': 'Sbalit/rozbalit příspěvek',
|
|
||||||
'keyboard_shortcuts.secondary_toot': 'Odeslat příspěvek s druhotným nastavením soukromí',
|
|
||||||
|
|
||||||
'column.subheading': 'Různé',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
153
app/javascript/flavours/glitch/locales/cs.json
Normal file
153
app/javascript/flavours/glitch/locales/cs.json
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
{
|
||||||
|
"about.fork_disclaimer": "Glitch-soc je svobodný software s otevřeným zdrojovým kódem založený na Mastodonu.",
|
||||||
|
"advanced_options.icon_title": "Pokročilá nastavení",
|
||||||
|
"advanced_options.local-only.long": "Neposílat na jiné servery",
|
||||||
|
"advanced_options.local-only.short": "Lokální příspěvek",
|
||||||
|
"advanced_options.local-only.tooltip": "Tento příspěvek je pouze lokální",
|
||||||
|
"advanced_options.threaded_mode.long": "Po odeslání automaticky otevře pole pro odpověď",
|
||||||
|
"advanced_options.threaded_mode.short": "Režim vlákna",
|
||||||
|
"advanced_options.threaded_mode.tooltip": "Režim vlákna je zapnutý",
|
||||||
|
"boost_modal.missing_description": "Příspěvek obsahuje obrázky bez popisků",
|
||||||
|
"column.subheading": "Různé",
|
||||||
|
"compose.attach": "Připojit...",
|
||||||
|
"compose.attach.doodle": "Něco namalovat",
|
||||||
|
"compose.attach.upload": "Nahrát soubor",
|
||||||
|
"compose.content-type.plain": "Prostý text",
|
||||||
|
"compose_form.poll.multiple_choices": "Povolit více odpovědí",
|
||||||
|
"compose_form.poll.single_choice": "Povolit jednu odpověď",
|
||||||
|
"compose_form.spoiler": "Přidat varování o obsahu",
|
||||||
|
"confirmation_modal.do_not_ask_again": "Příště se už neptat",
|
||||||
|
"content-type.change": "Formát příspěvku",
|
||||||
|
"direct.group_by_conversations": "Seskupit do konverzací",
|
||||||
|
"endorsed_accounts_editor.endorsed_accounts": "Vybrané účty",
|
||||||
|
"favourite_modal.combo": "Příště můžete pro přeskočení stisknout {combo}",
|
||||||
|
"getting_started.onboarding": "Ukaž mi to tu",
|
||||||
|
"home.column_settings.advanced": "Pokročilé",
|
||||||
|
"home.column_settings.filter_regex": "Filtrovat podle regulárních výrazů",
|
||||||
|
"home.column_settings.show_direct": "Zobrazit přímé zprávy",
|
||||||
|
"keyboard_shortcuts.bookmark": "Přidat do záložek",
|
||||||
|
"keyboard_shortcuts.secondary_toot": "Odeslat příspěvek s druhotným nastavením soukromí",
|
||||||
|
"keyboard_shortcuts.toggle_collapse": "Sbalit/rozbalit příspěvek",
|
||||||
|
"layout.auto": "Automatické",
|
||||||
|
"layout.hint.auto": "Vybrat rozložení automaticky v závislosti na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
|
||||||
|
"layout.hint.desktop": "Použít vícesloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
|
||||||
|
"layout.hint.single": "Použít jednosloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
|
||||||
|
"media_gallery.sensitive": "Citlivý obsah",
|
||||||
|
"navigation_bar.app_settings": "Nastavení aplikace",
|
||||||
|
"navigation_bar.featured_users": "Vybraní uživatelé",
|
||||||
|
"navigation_bar.info": "Rozšířené informace",
|
||||||
|
"navigation_bar.keyboard_shortcuts": "Klávesové zkratky",
|
||||||
|
"navigation_bar.misc": "Různé",
|
||||||
|
"notification.markForDeletion": "Označit pro smazání",
|
||||||
|
"notification_purge.btn_all": "Vybrat\nvše",
|
||||||
|
"notification_purge.btn_apply": "Smazat\nvybrané",
|
||||||
|
"notification_purge.btn_invert": "Obrátit\nvýběr",
|
||||||
|
"notification_purge.btn_none": "Nevybrat\nnic",
|
||||||
|
"notification_purge.start": "Čistící režim",
|
||||||
|
"notifications.marked_clear": "Smazat vybraná oznámení",
|
||||||
|
"notifications.marked_clear_confirmation": "Určitě chcete trvale smazat všechna vybraná oznámení?",
|
||||||
|
"onboarding.done": "Hotovo",
|
||||||
|
"onboarding.next": "Další",
|
||||||
|
"onboarding.page_five.public_timelines": "Místní časová osa zobrazuje veřejné příspěvky všech uživatelů instance {domain}. Federovaná časová osa zobrazí příspěvky od všech, koho uživatelé instance {domain} sledují. Tyto veřejné časové osy jsou skvělý způsob, jak objevit nové lidi.",
|
||||||
|
"onboarding.page_four.home": "Domovská časová osa zobrazuje příspěvky od lidí, které sledujete.",
|
||||||
|
"onboarding.page_four.notifications": "Notifikace se zobrazí, když s vámi někdo interaguje.",
|
||||||
|
"onboarding.page_one.federation": "{domain} je 'instance' Mastodonu. Mastodon je síť nezávislých serverů, které jsou spolu propojené do jedné velké sociální sítě. Těmto serverům říkáme instance.",
|
||||||
|
"onboarding.page_one.handle": "Jste na instanci {domain}, takže celá adresa vašeho profilu je {handle}",
|
||||||
|
"onboarding.page_one.welcome": "Vítá vás {domain}!",
|
||||||
|
"onboarding.page_six.almost_done": "Skoro hotovo...",
|
||||||
|
"onboarding.page_six.appetoot": "Veselé mastodonění!",
|
||||||
|
"onboarding.page_six.apps_available": "Jsou dostupné {apps} pro iOS, Android i jiné platformy.",
|
||||||
|
"onboarding.page_six.github": "Na serveru {domain} běží Glitchsoc. Glitchsoc je přátelský {fork} programu {Mastodon}, a je kompatibilní s jakoukoliv jinou mastodoní instancí nebo aplikací. Glitchsoc je zcela svobodný a má otevřený zdrojový kód. Na stránce {github} můžete hlásit chyby, žádat o nové funkce, nebo ke kódu vlastnoručně přispět.",
|
||||||
|
"onboarding.page_six.various_app": "mobilní aplikace",
|
||||||
|
"onboarding.page_three.profile": "Upravte si svůj profil a nastavte si profilový obrázek, jméno, a krátký text o sobě. Naleznete tam i další možnosti nastavení.",
|
||||||
|
"onboarding.page_three.search": "Pomocí vyhledávací lišty můžete hledat lidi nebo hashtagy. Pokud hledáte někoho z jiné instance, musíte použít celou adresu jeho profilu.",
|
||||||
|
"onboarding.page_two.compose": "Příspěvky se píší v levém sloupci. Pomocí ikon pod příspěvkem k němu můžete připojit obrázky, změnit úroveň soukromí nebo přidat varování o obsahu.",
|
||||||
|
"onboarding.skip": "Přeskočit",
|
||||||
|
"settings.always_show_spoilers_field": "Vždy zobrazit pole pro varování o obsahu",
|
||||||
|
"settings.auto_collapse": "Automaticky sbalit",
|
||||||
|
"settings.auto_collapse_all": "Všechno",
|
||||||
|
"settings.auto_collapse_lengthy": "Dlouhé příspěvky",
|
||||||
|
"settings.auto_collapse_media": "Příspěvky s přílohami",
|
||||||
|
"settings.auto_collapse_notifications": "Oznámení",
|
||||||
|
"settings.auto_collapse_reblogs": "Boosty",
|
||||||
|
"settings.auto_collapse_replies": "Odpovědi",
|
||||||
|
"settings.close": "Zavřít",
|
||||||
|
"settings.collapsed_statuses": "Sbalené příspěvky",
|
||||||
|
"settings.compose_box_opts": "Editační pole",
|
||||||
|
"settings.confirm_before_clearing_draft": "Zobrazit potvrzovací dialog před přepsáním právě vytvářené zprávy",
|
||||||
|
"settings.confirm_boost_missing_media_description": "Zobrazit potvrzovací dialog před boostnutím příspěvku s chybějícími popisky obrázků",
|
||||||
|
"settings.confirm_missing_media_description": "Zobrazit potvrzovací dialog při odesílání příspěvku, ve kterém chybí popisky obrázků",
|
||||||
|
"settings.content_warnings": "Varování o obsahu",
|
||||||
|
"settings.content_warnings.regexp": "Regulární výraz",
|
||||||
|
"settings.content_warnings_filter": "Tato varování o obsahu automaticky nerozbalovat:",
|
||||||
|
"settings.content_warnings_media_outside": "Zobrazit obrázky a videa mimo varování o obsahu",
|
||||||
|
"settings.content_warnings_media_outside_hint": "Obrázky a videa z příspěvku s varováním o obsahu se zobrazí se separátním přepínačem zobrazení, stejně jako na běžném Mastodonu.",
|
||||||
|
"settings.content_warnings_shared_state": "Zobrazit/schovat všechny kopie naráz",
|
||||||
|
"settings.content_warnings_shared_state_hint": "Tlačítko varování o obsahu bude mít efekt na všechny kopie příspěvku naráz, stejně jako na běžném Mastodonu. Nebude pak možné automaticky sbalit jakoukoliv kopii příspěvku, která má rozbalené varování o obsahu",
|
||||||
|
"settings.content_warnings_unfold_opts": "Možnosti automatického rozbalení",
|
||||||
|
"settings.deprecated_setting": "Tato možnost se nyní nastavuje v {settings_page_link}",
|
||||||
|
"settings.enable_collapsed": "Povolit sbalené příspěvky",
|
||||||
|
"settings.enable_collapsed_hint": "U sbalených příspěvků je část jejich obsahu skrytá, aby zabraly méně místa na obrazovce. (Tohle není stejná funkce jako varování o obsahu.)",
|
||||||
|
"settings.enable_content_warnings_auto_unfold": "Vždy rozbalit příspěvky označené varováním o obsahu",
|
||||||
|
"settings.general": "Obecné",
|
||||||
|
"settings.hicolor_privacy_icons": "Barevné ikony soukromí",
|
||||||
|
"settings.hicolor_privacy_icons.hint": "Zobrazit ikony úrovně soukromí příspěvků v jasných, snadno rozlišitelných barvách",
|
||||||
|
"settings.image_backgrounds": "Obrázkové pozadí",
|
||||||
|
"settings.image_backgrounds_media": "Náhled médií ve sbalených příspěvcích",
|
||||||
|
"settings.image_backgrounds_media_hint": "Pokud jsou k příspěvku přiložena média, použije se první z nich jako pozadí",
|
||||||
|
"settings.image_backgrounds_users": "Nastavit sbaleným příspěvkům obrázkové pozadí",
|
||||||
|
"settings.inline_preview_cards": "Zobrazit v časové ose náhledy externích odkazů",
|
||||||
|
"settings.layout": "Rozložení:",
|
||||||
|
"settings.layout_opts": "Možnosti rozvržení",
|
||||||
|
"settings.media": "Média",
|
||||||
|
"settings.media_fullwidth": "Zobrazit náhledy v plné šířce",
|
||||||
|
"settings.media_letterbox": "Neořezávat obrázky",
|
||||||
|
"settings.media_letterbox_hint": "Místo výřezu obrázku zobrazit obrázek celý, doplněný podle potřeby o prázdné okraje",
|
||||||
|
"settings.media_reveal_behind_cw": "Automaticky zobrazit média označená varováním o obsahu",
|
||||||
|
"settings.notifications.favicon_badge": "Zobrazit počet na ikoně serveru",
|
||||||
|
"settings.notifications.favicon_badge.hint": "Zobrazí počet nepřečtených oznámení na ikoně serveru",
|
||||||
|
"settings.notifications.tab_badge": "Zobrazit počet nepřečtených oznámení",
|
||||||
|
"settings.notifications.tab_badge.hint": "Počet nepřečtených oznámení se viditelně zobrazí na hlavní stránce (pokud není seznam oznámení viditelný)",
|
||||||
|
"settings.notifications_opts": "Možnosti oznámení",
|
||||||
|
"settings.pop_in_left": "Vlevo",
|
||||||
|
"settings.pop_in_player": "Povolit plovoucí okno přehrávače",
|
||||||
|
"settings.pop_in_position": "Pozice plovoucího okna:",
|
||||||
|
"settings.pop_in_right": "Vpravo",
|
||||||
|
"settings.preferences": "Předvolby",
|
||||||
|
"settings.prepend_cw_re": "Při odpovídání přidat před varování o obsahu “re: ”",
|
||||||
|
"settings.preselect_on_reply": "Při odpovědi označit uživatelská jména",
|
||||||
|
"settings.preselect_on_reply_hint": "Při odpovídání na konverzaci s více účastníky se jména všech kromě prvního označí, aby šla jednoduše smazat",
|
||||||
|
"settings.rewrite_mentions": "Přepsat zmínky v zobrazených příspěvcích",
|
||||||
|
"settings.rewrite_mentions_acct": "Přepsat uživatelským jménem a doménou (pokud je účet na jiném serveru)",
|
||||||
|
"settings.rewrite_mentions_no": "Nepřepisovat zmínky",
|
||||||
|
"settings.rewrite_mentions_username": "Přepsat uživatelským jménem",
|
||||||
|
"settings.shared_settings_link": "předvolbách Mastodonu",
|
||||||
|
"settings.show_action_bar": "Zobrazit ve sbalených příspěvcích tlačítka s akcemi",
|
||||||
|
"settings.show_content_type_choice": "Zobrazit volbu formátu příspěvku",
|
||||||
|
"settings.show_reply_counter": "Zobrazit odhad počtu odpovědí",
|
||||||
|
"settings.side_arm": "Vedlejší odesílací tlačítko:",
|
||||||
|
"settings.side_arm.none": "Žádné",
|
||||||
|
"settings.side_arm_reply_mode": "Při odpovídání na příspěvek by vedlejší odesílací tlačítko mělo:",
|
||||||
|
"settings.side_arm_reply_mode.copy": "Použít úroveň soukromí příspěvku, na který odpovídáte",
|
||||||
|
"settings.side_arm_reply_mode.keep": "Použít svou nastavenou úroveň soukromí",
|
||||||
|
"settings.side_arm_reply_mode.restrict": "Zvýšit úroveň soukromí nejméně na úroveň příspěvku, na který odpovídáte",
|
||||||
|
"settings.status_icons": "Ikony u příspěvků",
|
||||||
|
"settings.status_icons_language": "Indikace jazyk",
|
||||||
|
"settings.status_icons_local_only": "Indikace lokálního příspěvku",
|
||||||
|
"settings.status_icons_media": "Indikace obrázků a anket",
|
||||||
|
"settings.status_icons_reply": "Indikace odpovědi",
|
||||||
|
"settings.status_icons_visibility": "Indikace úrovně soukromí",
|
||||||
|
"settings.tag_misleading_links": "Označit zavádějící odkazy",
|
||||||
|
"settings.tag_misleading_links.hint": "Zobrazit skutečný cíl u každého odkazu, který ho explicitně nezmiňuje",
|
||||||
|
"settings.wide_view": "Široké sloupce (pouze v režimu Desktop)",
|
||||||
|
"settings.wide_view_hint": "Sloupce se roztáhnout, aby lépe vyplnily dostupný prostor.",
|
||||||
|
"status.collapse": "Sbalit",
|
||||||
|
"status.has_audio": "Obsahuje audio",
|
||||||
|
"status.has_pictures": "Obsahuje obrázky",
|
||||||
|
"status.has_preview_card": "Obsahuje náhled odkazu",
|
||||||
|
"status.has_video": "Obsahuje video",
|
||||||
|
"status.in_reply_to": "Tento příspěvek je odpověď",
|
||||||
|
"status.is_poll": "Tento příspěvek je anketa",
|
||||||
|
"status.local_only": "Viditelné pouze z vaší instance",
|
||||||
|
"status.uncollapse": "Rozbalit"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/cy.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/cy.json
Normal file
6
app/javascript/flavours/glitch/locales/cy.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/da.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/da.json
Normal file
6
app/javascript/flavours/glitch/locales/da.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
206
app/javascript/flavours/glitch/locales/de.json
Normal file
206
app/javascript/flavours/glitch/locales/de.json
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
{
|
||||||
|
"about.fork_disclaimer": "Glitch-soc ist freie, quelloffene Software geforkt von Mastodon.",
|
||||||
|
"account.add_account_note": "Notiz für @{name} hinzufügen",
|
||||||
|
"account.disclaimer_full": "Die folgenden Informationen könnten das Profil des Nutzers unvollständig wiedergeben.",
|
||||||
|
"account.follows": "Folgt",
|
||||||
|
"account.joined": "Beigetreten am {date}",
|
||||||
|
"account.suspended_disclaimer_full": "Dieser Nutzer wurde durch einen Moderator gesperrt.",
|
||||||
|
"account.view_full_profile": "Vollständiges Profil anzeigen",
|
||||||
|
"account_note.cancel": "Abbrechen",
|
||||||
|
"account_note.edit": "Bearbeiten",
|
||||||
|
"account_note.glitch_placeholder": "Kein Kommentar angegeben",
|
||||||
|
"account_note.save": "Speichern",
|
||||||
|
"advanced_options.icon_title": "Erweiterte Optionen",
|
||||||
|
"advanced_options.local-only.long": "Nicht auf anderen Instanzen posten",
|
||||||
|
"advanced_options.local-only.short": "Nur lokal",
|
||||||
|
"advanced_options.local-only.tooltip": "Dieser Post ist nur lokal",
|
||||||
|
"advanced_options.threaded_mode.long": "Öffnet automatisch eine Antwort beim Schreiben",
|
||||||
|
"advanced_options.threaded_mode.short": "Thread-Modus",
|
||||||
|
"advanced_options.threaded_mode.tooltip": "Thread-Modus aktiviert",
|
||||||
|
"boost_modal.missing_description": "Dieser Toot enthält Medien ohne Beschreibung",
|
||||||
|
"column.favourited_by": "Favorisiert von",
|
||||||
|
"column.heading": "Sonstiges",
|
||||||
|
"column.reblogged_by": "Geteilt von",
|
||||||
|
"column.subheading": "Sonstige Optionen",
|
||||||
|
"column_header.profile": "Profil",
|
||||||
|
"column_subheading.lists": "Listen",
|
||||||
|
"column_subheading.navigation": "Navigation",
|
||||||
|
"community.column_settings.allow_local_only": "Nur-lokale Toots anzeigen",
|
||||||
|
"compose.attach": "Anhängen...",
|
||||||
|
"compose.attach.doodle": "Etwas zeichnen",
|
||||||
|
"compose.attach.upload": "Eine Datei hochladen",
|
||||||
|
"compose.content-type.html": "HTML",
|
||||||
|
"compose.content-type.markdown": "Markdown",
|
||||||
|
"compose.content-type.plain": "Unformatierter Text",
|
||||||
|
"compose_form.poll.multiple_choices": "Mehrfachauswahl erlauben",
|
||||||
|
"compose_form.poll.single_choice": "Eine Auswahl erlauben",
|
||||||
|
"compose_form.spoiler": "Text hinter Warnung verbergen",
|
||||||
|
"confirmation_modal.do_not_ask_again": "Nicht erneut nach Bestätigung fragen",
|
||||||
|
"confirmations.deprecated_settings.confirm": "Mastodon-Einstellungen verwenden",
|
||||||
|
"confirmations.deprecated_settings.message": "Einige der von dir verwendeten, glitch-soc-spezifischen {app_settings} wurden durch Mastodon {preferences} ersetzt und werden überschrieben:",
|
||||||
|
"confirmations.missing_media_description.confirm": "Trotzdem absenden",
|
||||||
|
"confirmations.missing_media_description.edit": "Anhänge bearbeiten",
|
||||||
|
"confirmations.missing_media_description.message": "Mindestens einem Anhang fehlt eine Beschreibung. Denke darüber nach, alle Anhänge für Sehbeeinträchtigte zu beschreiben, bevor du den Toot absendest.",
|
||||||
|
"confirmations.unfilter.author": "Urheber",
|
||||||
|
"confirmations.unfilter.confirm": "Anzeigen",
|
||||||
|
"confirmations.unfilter.edit_filter": "Filter bearbeiten",
|
||||||
|
"confirmations.unfilter.filters": "Passende{count, plural, one {r} other {}} Filter",
|
||||||
|
"content-type.change": "Inhaltstyp",
|
||||||
|
"direct.group_by_conversations": "Nach Unterhaltung gruppieren",
|
||||||
|
"endorsed_accounts_editor.endorsed_accounts": "Empfohlene Konten",
|
||||||
|
"favourite_modal.combo": "Mit {combo} wird dieses Fenster beim nächsten Mal nicht mehr angezeigt",
|
||||||
|
"getting_started.onboarding": "Führe mich herum",
|
||||||
|
"home.column_settings.advanced": "Erweitert",
|
||||||
|
"home.column_settings.filter_regex": "Mit regulären Ausdrücken herausfiltern",
|
||||||
|
"home.column_settings.show_direct": "Direktnachrichten anzeigen",
|
||||||
|
"home.settings": "Spalteneinstellungen",
|
||||||
|
"keyboard_shortcuts.bookmark": "zu Lesezeichen hinzufügen",
|
||||||
|
"keyboard_shortcuts.secondary_toot": "Toot mit sekundärer Privatsphäreeinstellung absenden",
|
||||||
|
"keyboard_shortcuts.toggle_collapse": "Toots ein-/ausklappen",
|
||||||
|
"tooltips.reactions": "Reaktionen",
|
||||||
|
"layout.auto": "Automatisch",
|
||||||
|
"layout.desktop": "Desktop",
|
||||||
|
"layout.hint.auto": "Automatisch das Layout anhand der Einstellung \"Erweitertes Webinterface verwenden\" und Bildschirmgröße auswählen.",
|
||||||
|
"layout.hint.desktop": "Das mehrspaltige Layout verwenden, unabhängig von der Einstellung \"Erweitertes Webinterface verwenden\".",
|
||||||
|
"layout.hint.single": "Das einspaltige Layout verwenden, unabhängig von der Einstellung \"Erweitertes Webinterface verwenden\".",
|
||||||
|
"layout.single": "Mobil",
|
||||||
|
"media_gallery.sensitive": "Empfindlich",
|
||||||
|
"moved_to_warning": "Dieses Konto ist als verschoben zu {moved_to_link} markiert und akzeptiert daher keine neuen Follower.",
|
||||||
|
"navigation_bar.app_settings": "App-Einstellungen",
|
||||||
|
"navigation_bar.featured_users": "Empfohlene Nutzer",
|
||||||
|
"navigation_bar.info": "Erweiterte Informationen",
|
||||||
|
"navigation_bar.keyboard_shortcuts": "Tastaturkürzel",
|
||||||
|
"navigation_bar.misc": "Sonstiges",
|
||||||
|
"notification.markForDeletion": "Zum Entfernen auswählen",
|
||||||
|
"notification.reaction": "{name} hat auf deinen Beitrag reagiert",
|
||||||
|
"notifications.column_settings.reaction": "Reaktionen:",
|
||||||
|
"notification_purge.btn_all": "Alle\nauswählen",
|
||||||
|
"notification_purge.btn_apply": "Ausgewählte\nentfernen",
|
||||||
|
"notification_purge.btn_invert": "Auswahl\numkehren",
|
||||||
|
"notification_purge.btn_none": "Auswahl\naufheben",
|
||||||
|
"notification_purge.start": "Benachrichtigungen-Aufräumen-Modus starten",
|
||||||
|
"notifications.marked_clear": "Ausgewählte Benachrichtigungen entfernen",
|
||||||
|
"notifications.marked_clear_confirmation": "Möchtest du wirklich alle auswählten Benachrichtigungen für immer entfernen?",
|
||||||
|
"onboarding.done": "Fertig",
|
||||||
|
"onboarding.next": "Weiter",
|
||||||
|
"onboarding.page_five.public_timelines": "Die lokale Timeline zeigt öffentliche Posts von allen auf {domain}. Die föderierte Timeline zeigt öffentliche Posts von allen, denen Leute auf {domain} folgen. Das sind die öffentlichen Timelines, eine tolle Möglichkeit, neue Leute zu entdecken.",
|
||||||
|
"onboarding.page_four.home": "Die Startseite zeigt Posts von Leuten an, denen du folgst.",
|
||||||
|
"onboarding.page_four.notifications": "Die Benachrichtigungs-Spalte zeigt an, wenn jemand mit dir interagiert.",
|
||||||
|
"onboarding.page_one.federation": "{domain} ist eine \"Instanz\" von Mastodon. Mastodon ist ein Netzwerk aus unabhängigen Servern, die zusammen ein größeres soziales Netzwerk bilden. Diese Server nennen wir Instanzen.",
|
||||||
|
"onboarding.page_one.handle": "Du bist auf {domain}, also ist dein vollständiger Nutzername {handle}",
|
||||||
|
"onboarding.page_one.welcome": "Willkommen auf {domain}!",
|
||||||
|
"onboarding.page_six.admin": "Dein Instanz-Admin ist {admin}.",
|
||||||
|
"onboarding.page_six.almost_done": "Fast geschafft...",
|
||||||
|
"onboarding.page_six.appetoot": "Bon Appetoot!",
|
||||||
|
"onboarding.page_six.apps_available": "Es gibt {apps} für iOS, Android und andere Plattformen.",
|
||||||
|
"onboarding.page_six.github": "{domain} läuft auf glitch-soc. glitch-soc ist ein freundlicher {fork} von {Mastodon}, und ist mit jeder Mastodon-App oder -Instanz kompatibel. glitch-soc ist komplett frei und quelloffen. Auf {github} kannst du Fehler melden, Features anfragen oder Code beitragen.",
|
||||||
|
"onboarding.page_six.guidelines": "Community-Richtlinien",
|
||||||
|
"onboarding.page_six.read_guidelines": "Bitte lies die {guidelines} von {domain}!",
|
||||||
|
"onboarding.page_six.various_app": "mobile Apps",
|
||||||
|
"onboarding.page_three.profile": "Bearbeite dein Profil, um deinen Avatar, \"Über mich\" und den Anzeigenamen zu ändern. Dort findest du auch andere Einstellungen.",
|
||||||
|
"onboarding.page_three.search": "Benutze die Suchleiste, um Leute zu finden und Hashtags anzusehen, wie etwa {illustration} und {introductions}. Um nach einer Person zu suchen, die nicht auf dieser Instanz ist, benutze deren vollständigen Nutzername.",
|
||||||
|
"onboarding.page_two.compose": "Schreibe Posts in der Verfassen-Spalte. Mit den Symbolen unten kannst du Bilder hochladen, Privatsphäre-Einstellungen ändern, und Inhaltswarnungen hinzufügen.",
|
||||||
|
"onboarding.skip": "Überspringen",
|
||||||
|
"settings.always_show_spoilers_field": "Das Inhaltswarnungs-Feld immer aktivieren",
|
||||||
|
"settings.auto_collapse": "Automatisches Einklappen",
|
||||||
|
"settings.auto_collapse_all": "Alles",
|
||||||
|
"settings.auto_collapse_lengthy": "Lange Toots",
|
||||||
|
"settings.auto_collapse_media": "Toots mit Anhängen",
|
||||||
|
"settings.auto_collapse_notifications": "Benachrichtigungen",
|
||||||
|
"settings.auto_collapse_reblogs": "Geteilte Toots",
|
||||||
|
"settings.auto_collapse_replies": "Antworten",
|
||||||
|
"settings.close": "Schließen",
|
||||||
|
"settings.collapsed_statuses": "Eingeklappte Toots",
|
||||||
|
"settings.compose_box_opts": "Verfassen-Box",
|
||||||
|
"settings.confirm_before_clearing_draft": "Zeige einen Bestätigungsdialog, bevor der derzeitige Entwurf verworfen wird",
|
||||||
|
"settings.confirm_boost_missing_media_description": "Zeige einen Bestätigungsdialog, bevor Toots mit Anhängen ohne Beschreibung geteilt werden",
|
||||||
|
"settings.confirm_missing_media_description": "Zeige einen Bestätigungsdialog, bevor Toots mit Anhängen ohne Beschreibung abgesendet werden",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.content_warnings.regexp": "Regulärer Ausdruck",
|
||||||
|
"settings.content_warnings_filter": "Inhaltswarnungen, die nicht ausgeklappt werden sollen:",
|
||||||
|
"settings.content_warnings_media_outside": "Medienanhänge außerhalb von Inhaltswarnungen anzeigen",
|
||||||
|
"settings.content_warnings_media_outside_hint": "Das ursprüngliche Verhalten von Mastodon wiederherstellen, in welchem Inhaltswarnungen keine Auswirkungen auf Anhänge haben",
|
||||||
|
"settings.content_warnings_shared_state": "Inhalt aller Kopien auf einmal ein-/ausblenden",
|
||||||
|
"settings.content_warnings_shared_state_hint": "Das ursprüngliche Verhalten von Mastodon wiederhertstellen, in welchem der Inhaltswarnungs-Knopf alle Kopien eines Posts auf einmal ein-/ausklappt. Das wird das automatische Einklappen jedweder Kopie eines Toots mit ausgeklappter Inhaltswarnung",
|
||||||
|
"settings.content_warnings_unfold_opts": "Optionen zum automatischen Ausklappen",
|
||||||
|
"settings.deprecated_setting": "Diese Einstellung wird nun von Mastodons {settings_page_link} gesteuert",
|
||||||
|
"settings.enable_collapsed": "Eingeklappte Toots aktivieren",
|
||||||
|
"settings.enable_collapsed_hint": "Eingeklappte Posts haben einen Teil ihres Inhalts verborgen, um weniger Platz am Bildschirm einzunehmen. Das passiert unabhängig von der Inhaltswarnfunktion",
|
||||||
|
"settings.enter_amount_prompt": "Gib eine Zahl ein",
|
||||||
|
"settings.enable_content_warnings_auto_unfold": "Inhaltswarnungen automatisch ausklappen",
|
||||||
|
"settings.general": "Allgemein",
|
||||||
|
"settings.hicolor_privacy_icons": "Eingefärbte Privatsphäre-Symbole",
|
||||||
|
"settings.hicolor_privacy_icons.hint": "Zeige Privatsphäre-Symbole in hellen und leicht zu unterscheidenden Farben",
|
||||||
|
"settings.image_backgrounds": "Bildhintergründe",
|
||||||
|
"settings.image_backgrounds_media": "Vorschau eingeklappter Toot-Anhänge",
|
||||||
|
"settings.image_backgrounds_media_hint": "Wenn der Post Anhänge hat, wird der erste als Hintergrund verwendet",
|
||||||
|
"settings.image_backgrounds_users": "Eingeklappten Toots einen Bild-Hintergrund geben",
|
||||||
|
"settings.inline_preview_cards": "Eingebettete Vorschaukarten für externe Links",
|
||||||
|
"settings.layout": "Layout:",
|
||||||
|
"settings.layout_opts": "Layout-Optionen",
|
||||||
|
"settings.media": "Medien",
|
||||||
|
"settings.media_fullwidth": "Medienvorschau in voller Breite",
|
||||||
|
"settings.num_visible_reactions": "Anzahl sichtbarer Reaktionen",
|
||||||
|
"settings.media_letterbox": "Mediengröße anpassen",
|
||||||
|
"settings.media_letterbox_hint": "Medien runterskalieren und einpassen um die Bildbehälter zu füllen anstatt zu strecken und zuzuschneiden",
|
||||||
|
"settings.media_reveal_behind_cw": "Empfindliche Medien hinter Inhaltswarnungen standardmäßig anzeigen",
|
||||||
|
"settings.notifications.favicon_badge": "Favicon-Badge für ungelesene Benachrichtigungen",
|
||||||
|
"settings.notifications.favicon_badge.hint": "Ein Badge für ungelesene Benachrichtigungen zum Favicon hinzufügen",
|
||||||
|
"settings.notifications.tab_badge": "Badge für ungelesene Benachrichtigungen",
|
||||||
|
"settings.notifications.tab_badge.hint": "Ein Badge für ungelesene Benachrichtigungen in den Spaltensymbolen anzeigen, wenn die Benachrichtigungen nicht offen sind",
|
||||||
|
"settings.notifications_opts": "Benachrichtigungsoptionen",
|
||||||
|
"settings.pop_in_left": "Links",
|
||||||
|
"settings.pop_in_player": "Pop-In-Player aktivieren",
|
||||||
|
"settings.pop_in_position": "Position des Pop-In-Players:",
|
||||||
|
"settings.pop_in_right": "Rechts",
|
||||||
|
"settings.preferences": "Preferences",
|
||||||
|
"settings.prepend_cw_re": "\"re: \" beim Antworten an Inhaltswarnung voranstellen",
|
||||||
|
"settings.preselect_on_reply": "Nutzernamen bei Antwort vorauswählen",
|
||||||
|
"settings.preselect_on_reply_hint": "Beim Antworten auf eine Konversation alle Nutzernamen auswählen, die nach dem ersten kommen",
|
||||||
|
"settings.rewrite_mentions": "Erwähnungen in angezeigten Status umschreiben",
|
||||||
|
"settings.rewrite_mentions_acct": "Mit Nutzernamen und Domain umschreiben (wenn das Konto auf einer anderen Instanz ist)",
|
||||||
|
"settings.rewrite_mentions_no": "Erwähnungen nicht umschreiben",
|
||||||
|
"settings.rewrite_mentions_username": "Mit Nutzername umschreiben",
|
||||||
|
"settings.shared_settings_link": "Nutzereinstellungen",
|
||||||
|
"settings.show_action_bar": "Aktions-Knöpfe in eingeklappten Toots anzeigen",
|
||||||
|
"settings.show_content_type_choice": "Auswahl für die Inhaltsart beim Verfassen von Toots anzeigen",
|
||||||
|
"settings.show_reply_counter": "Schätzung der Antwortanzahl anzeigen",
|
||||||
|
"settings.side_arm": "Sekundärer Toot-Knopf:",
|
||||||
|
"settings.side_arm.none": "Nichts",
|
||||||
|
"settings.side_arm_reply_mode": "Beim Antworten auf einen Toot sollte der sekundäre Toot-Knopf:",
|
||||||
|
"settings.side_arm_reply_mode.copy": "Privatsphäre-Einstellung des zu beantwortenden Toot verwenden",
|
||||||
|
"settings.side_arm_reply_mode.keep": "Die eingestellte Privatsphäre beibehalten",
|
||||||
|
"settings.side_arm_reply_mode.restrict": "Privatsphäre-Einstellung auf die des zu beantwortenden Toot beschränken",
|
||||||
|
"settings.status_icons": "Toot-Symbole",
|
||||||
|
"settings.status_icons_language": "Sprach-Indikator",
|
||||||
|
"settings.status_icons_local_only": "\"nur Lokal\"-Indikator",
|
||||||
|
"settings.status_icons_media": "Medien- und Umfragen-Indikatoren",
|
||||||
|
"settings.status_icons_reply": "Antwort-Indikator",
|
||||||
|
"settings.status_icons_visibility": "Toot-Privatsphäre-Indikator",
|
||||||
|
"settings.swipe_to_change_columns": "Das Wechseln der Spalte durch Wischen erlauben (nur für die mobile Ansicht)",
|
||||||
|
"settings.tag_misleading_links": "Irreführende Links markieren",
|
||||||
|
"settings.tag_misleading_links.hint": "Füge eine visuelle Indikation mit dem Ziel-Host des Links zu jedem Link hinzu, bei dem dieser nicht explizit genannt wird",
|
||||||
|
"settings.wide_view": "Breite Ansicht (nur für den Desktop-Modus)",
|
||||||
|
"settings.wide_view_hint": "Verbreitert Spalten, um den verfügbaren Platz besser zu füllen.",
|
||||||
|
"status.collapse": "Einklappen",
|
||||||
|
"status.react": "Reagieren",
|
||||||
|
"status.has_audio": "Hat angehängte Audiodateien",
|
||||||
|
"status.has_pictures": "Hat angehängte Bilder",
|
||||||
|
"status.has_preview_card": "Hat eine Vorschaukarte",
|
||||||
|
"status.has_video": "Hat angehängte Videos",
|
||||||
|
"status.in_reply_to": "Dieser Toot ist eine Antwort",
|
||||||
|
"status.is_poll": "Dieser Toot ist eine Umfrage",
|
||||||
|
"status.local_only": "Nur auf deiner Instanz sichtbar",
|
||||||
|
"status.sensitive_toggle": "Zum Anzeigen klicken",
|
||||||
|
"status.uncollapse": "Ausklappen",
|
||||||
|
"web_app_crash.change_your_settings": "Deine {settings} ändern",
|
||||||
|
"web_app_crash.content": "Du kannst folgende Dinge ausprobieren:",
|
||||||
|
"web_app_crash.debug_info": "Debug-Informationen",
|
||||||
|
"web_app_crash.disable_addons": "Browser-Add-ons oder eingebaute Übersetzungswerkzeuge deaktivieren",
|
||||||
|
"web_app_crash.issue_tracker": "Issue-Tracker",
|
||||||
|
"web_app_crash.reload": "neu laden",
|
||||||
|
"web_app_crash.reload_page": "Die Seite {reload}",
|
||||||
|
"web_app_crash.report_issue": "Einen Fehler im {issuetracker} melden",
|
||||||
|
"web_app_crash.settings": "Einstellungen",
|
||||||
|
"web_app_crash.title": "Es tut uns leid, aber mit der Mastodon-App ist etwas schiefgelaufen."
|
||||||
|
}
|
1064
app/javascript/flavours/glitch/locales/defaultMessages.json
Normal file
1064
app/javascript/flavours/glitch/locales/defaultMessages.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/el.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
6
app/javascript/flavours/glitch/locales/el.json
Normal file
6
app/javascript/flavours/glitch/locales/el.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.preferences": "Preferences"
|
||||||
|
}
|
1
app/javascript/flavours/glitch/locales/en-GB.json
Normal file
1
app/javascript/flavours/glitch/locales/en-GB.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -5,12 +5,36 @@ const messages = {
|
||||||
'layout.auto': 'Auto',
|
'layout.auto': 'Auto',
|
||||||
'layout.current_is': 'Your current layout is:',
|
'layout.current_is': 'Your current layout is:',
|
||||||
'layout.desktop': 'Desktop',
|
'layout.desktop': 'Desktop',
|
||||||
'layout.mobile': 'Mobile',
|
'layout.single': 'Mobile',
|
||||||
|
'layout.hint.auto': 'Automatically chose layout based on “Enable advanced web interface” setting and screen size.',
|
||||||
|
'layout.hint.desktop': 'Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.',
|
||||||
|
'layout.hint.single': 'Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.',
|
||||||
'navigation_bar.app_settings': 'App settings',
|
'navigation_bar.app_settings': 'App settings',
|
||||||
|
'navigation_bar.misc': 'Misc',
|
||||||
|
'navigation_bar.keyboard_shortcuts': 'Keyboard shortcuts',
|
||||||
|
'navigation_bar.info': 'Extended information',
|
||||||
|
'navigation_bar.featured_users': 'Featured users',
|
||||||
'getting_started.onboarding': 'Show me around',
|
'getting_started.onboarding': 'Show me around',
|
||||||
|
'onboarding.next': 'Next',
|
||||||
|
'onboarding.done': 'Done',
|
||||||
|
'onboarding.skip': 'Skip',
|
||||||
'onboarding.page_one.federation': '{domain} is an \'instance\' of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.',
|
'onboarding.page_one.federation': '{domain} is an \'instance\' of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.',
|
||||||
'onboarding.page_one.welcome': 'Welcome to {domain}!',
|
'onboarding.page_one.welcome': 'Welcome to {domain}!',
|
||||||
|
'onboarding.page_one.handle': 'You are on {domain}, so your full handle is {handle}',
|
||||||
|
'onboarding.page_two.compose': 'Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.',
|
||||||
|
'onboarding.page_three.search': 'Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.',
|
||||||
|
'onboarding.page_three.profile': 'Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.',
|
||||||
|
'onboarding.page_four.home': 'The home timeline shows posts from people you follow.',
|
||||||
|
'onboarding.page_four.notifications': 'The notifications column shows when someone interacts with you.',
|
||||||
|
'onboarding.page_five.public_timelines': 'The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.',
|
||||||
|
'onboarding.page_six.admin': 'Your instance\'s admin is {admin}.',
|
||||||
|
'onboarding.page_six.read_guidelines': 'Please read {domain}\'s {guidelines}!',
|
||||||
|
'onboarding.page_six.guidelines': 'community guidelines',
|
||||||
|
'onboarding.page_six.almost_done': 'Almost done...',
|
||||||
'onboarding.page_six.github': '{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. You can report bugs, request features, or contribute to the code on {github}.',
|
'onboarding.page_six.github': '{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. You can report bugs, request features, or contribute to the code on {github}.',
|
||||||
|
'onboarding.page_six.apps_available': 'There are {apps} available for iOS, Android and other platforms.',
|
||||||
|
'onboarding.page_six.various_app': 'mobile apps',
|
||||||
|
'onboarding.page_six.appetoot': 'Bon Appetoot!',
|
||||||
'settings.auto_collapse': 'Automatic collapsing',
|
'settings.auto_collapse': 'Automatic collapsing',
|
||||||
'settings.auto_collapse_all': 'Everything',
|
'settings.auto_collapse_all': 'Everything',
|
||||||
'settings.auto_collapse_lengthy': 'Lengthy toots',
|
'settings.auto_collapse_lengthy': 'Lengthy toots',
|
||||||
|
@ -22,19 +46,100 @@ const messages = {
|
||||||
'settings.close': 'Close',
|
'settings.close': 'Close',
|
||||||
'settings.collapsed_statuses': 'Collapsed toots',
|
'settings.collapsed_statuses': 'Collapsed toots',
|
||||||
'settings.enable_collapsed': 'Enable collapsed toots',
|
'settings.enable_collapsed': 'Enable collapsed toots',
|
||||||
|
'settings.enable_collapsed_hint': 'Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature',
|
||||||
'settings.general': 'General',
|
'settings.general': 'General',
|
||||||
|
'settings.compose_box_opts': 'Compose box',
|
||||||
|
'settings.side_arm': 'Secondary toot button:',
|
||||||
|
'settings.side_arm.none': 'None',
|
||||||
|
'settings.side_arm_reply_mode': 'When replying to a toot, the secondary toot button should:',
|
||||||
|
'settings.side_arm_reply_mode.keep': 'Keep its set privacy',
|
||||||
|
'settings.side_arm_reply_mode.copy': 'Copy privacy setting of the toot being replied to',
|
||||||
|
'settings.side_arm_reply_mode.restrict': 'Restrict privacy setting to that of the toot being replied to',
|
||||||
|
'settings.always_show_spoilers_field': 'Always enable the Content Warning field',
|
||||||
|
'settings.prepend_cw_re': 'Prepend “re: ” to content warnings when replying',
|
||||||
|
'settings.preselect_on_reply': 'Pre-select usernames on reply',
|
||||||
|
'settings.preselect_on_reply_hint': 'When replying to a conversation with multiple participants, pre-select usernames past the first',
|
||||||
|
'settings.confirm_missing_media_description': 'Show confirmation dialog before sending toots lacking media descriptions',
|
||||||
|
'settings.confirm_before_clearing_draft': 'Show confirmation dialog before overwriting the message being composed',
|
||||||
|
'settings.show_content_type_choice': 'Show content-type choice when authoring toots',
|
||||||
|
'settings.content_warnings': 'Content Warnings',
|
||||||
|
'settings.content_warnings.regexp': 'Regular expression',
|
||||||
|
'settings.content_warnings_shared_state': 'Show/hide content of all copies at once',
|
||||||
|
'settings.content_warnings_shared_state_hint': 'Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW',
|
||||||
|
'settings.content_warnings_media_outside': 'Display media attachments outside content warnings',
|
||||||
|
'settings.content_warnings_media_outside_hint': 'Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments',
|
||||||
|
'settings.content_warnings_unfold_opts': 'Auto-unfolding options',
|
||||||
|
'settings.enable_content_warnings_auto_unfold': 'Automatically unfold content-warnings',
|
||||||
|
'settings.deprecated_setting': 'This setting is now controlled from Mastodon\'s {settings_page_link}',
|
||||||
|
'settings.shared_settings_link': 'user preferences',
|
||||||
|
'settings.content_warnings_filter': 'Content warnings to not automatically unfold:',
|
||||||
|
'settings.layout_opts': 'Layout options',
|
||||||
|
'settings.rewrite_mentions_no': 'Do not rewrite mentions',
|
||||||
|
'settings.rewrite_mentions_acct': 'Rewrite with username and domain (when the account is remote)',
|
||||||
|
'settings.rewrite_mentions_username': 'Rewrite with username',
|
||||||
|
'settings.show_reply_counter': 'Display an estimate of the reply count',
|
||||||
|
'settings.hicolor_privacy_icons': 'High color privacy icons',
|
||||||
|
'settings.hicolor_privacy_icons.hint': 'Display privacy icons in bright and easily distinguishable colors',
|
||||||
|
'settings.confirm_boost_missing_media_description': 'Show confirmation dialog before boosting toots lacking media descriptions',
|
||||||
|
'settings.tag_misleading_links': 'Tag misleading links',
|
||||||
|
'settings.tag_misleading_links.hint': 'Add a visual indication with the link target host to every link not mentioning it explicitly',
|
||||||
|
'settings.rewrite_mentions': 'Rewrite mentions in displayed statuses',
|
||||||
|
'settings.notifications_opts': 'Notifications options',
|
||||||
|
'settings.notifications.tab_badge': 'Unread notifications badge',
|
||||||
|
'settings.notifications.tab_badge.hint': 'Display a badge for unread notifications in the column icons when the notifications column isn\'t open',
|
||||||
|
'settings.notifications.favicon_badge': 'Unread notifications favicon badge',
|
||||||
|
'settings.notifications.favicon_badge.hint': 'Add a badge for unread notifications to the favicon',
|
||||||
|
'settings.status_icons': 'Toot icons',
|
||||||
|
'settings.status_icons_language': 'Language indicator',
|
||||||
|
'settings.status_icons_reply': 'Reply indicator',
|
||||||
|
'settings.status_icons_local_only': 'Local-only indicator',
|
||||||
|
'settings.status_icons_media': 'Media and poll indicators',
|
||||||
|
'settings.status_icons_visibility': 'Toot privacy indicator',
|
||||||
|
'settings.layout': 'Layout:',
|
||||||
'settings.image_backgrounds': 'Image backgrounds',
|
'settings.image_backgrounds': 'Image backgrounds',
|
||||||
'settings.image_backgrounds_media': 'Preview collapsed toot media',
|
'settings.image_backgrounds_media': 'Preview collapsed toot media',
|
||||||
|
'settings.image_backgrounds_media_hint': 'If the post has any media attachment, use the first one as a background',
|
||||||
'settings.image_backgrounds_users': 'Give collapsed toots an image background',
|
'settings.image_backgrounds_users': 'Give collapsed toots an image background',
|
||||||
'settings.media': 'Media',
|
'settings.media': 'Media',
|
||||||
'settings.media_letterbox': 'Letterbox media',
|
'settings.media_letterbox': 'Letterbox media',
|
||||||
|
'settings.media_letterbox_hint': 'Scale down and letterbox media to fill the image containers instead of stretching and cropping them',
|
||||||
'settings.media_fullwidth': 'Full-width media previews',
|
'settings.media_fullwidth': 'Full-width media previews',
|
||||||
|
'settings.inline_preview_cards': 'Inline preview cards for external links',
|
||||||
|
'settings.media_reveal_behind_cw': 'Reveal sensitive media behind a CW by default',
|
||||||
|
'settings.pop_in_player': 'Enable pop-in player',
|
||||||
|
'settings.pop_in_position': 'Pop-in player position:',
|
||||||
|
'settings.pop_in_left': 'Left',
|
||||||
|
'settings.pop_in_right': 'Right',
|
||||||
'settings.preferences': 'User preferences',
|
'settings.preferences': 'User preferences',
|
||||||
'settings.wide_view': 'Wide view (Desktop mode only)',
|
'settings.wide_view': 'Wide view (Desktop mode only)',
|
||||||
|
'settings.wide_view_hint': 'Stretches columns to better fill the available space.',
|
||||||
'settings.navbar_under': 'Navbar at the bottom (Mobile only)',
|
'settings.navbar_under': 'Navbar at the bottom (Mobile only)',
|
||||||
'status.collapse': 'Collapse',
|
'status.collapse': 'Collapse',
|
||||||
'status.react': 'React',
|
'status.react': 'React',
|
||||||
'status.uncollapse': 'Uncollapse',
|
'status.uncollapse': 'Uncollapse',
|
||||||
|
'status.in_reply_to': 'This toot is a reply',
|
||||||
|
'status.has_preview_card': 'Features an attached preview card',
|
||||||
|
'status.has_pictures': 'Features attached pictures',
|
||||||
|
'status.is_poll': 'This toot is a poll',
|
||||||
|
'status.has_video': 'Features attached videos',
|
||||||
|
'status.has_audio': 'Features attached audio files',
|
||||||
|
'status.local_only': 'Only visible from your instance',
|
||||||
|
|
||||||
|
'content_type.change': 'Content type',
|
||||||
|
'compose.content-type.html': 'HTML',
|
||||||
|
'compose.content-type.markdown': 'Markdown',
|
||||||
|
'compose.content-type.plain': 'Plain text',
|
||||||
|
|
||||||
|
'compose_form.poll.single_choice': 'Allow one choice',
|
||||||
|
'compose_form.poll.multiple_choices': 'Allow multiple choices',
|
||||||
|
'compose_form.spoiler': 'Hide text behind warning',
|
||||||
|
|
||||||
|
'column.toot': 'Toots and replies',
|
||||||
|
'column_header.profile': 'Profile',
|
||||||
|
'column.heading': 'Misc',
|
||||||
|
'column.subheading': 'Miscellaneous options',
|
||||||
|
'column_subheading.navigation': 'Navigation',
|
||||||
|
'column_subheading.lists': 'Lists',
|
||||||
|
|
||||||
'media_gallery.sensitive': 'Sensitive',
|
'media_gallery.sensitive': 'Sensitive',
|
||||||
|
|
||||||
|
@ -49,6 +154,7 @@ const messages = {
|
||||||
'notifications.marked_clear_confirmation': 'Are you sure you want to permanently clear all selected notifications?',
|
'notifications.marked_clear_confirmation': 'Are you sure you want to permanently clear all selected notifications?',
|
||||||
'notifications.marked_clear': 'Clear selected notifications',
|
'notifications.marked_clear': 'Clear selected notifications',
|
||||||
|
|
||||||
|
'notification_purge.start': 'Enter notification cleaning mode',
|
||||||
'notification_purge.btn_all': 'Select\nall',
|
'notification_purge.btn_all': 'Select\nall',
|
||||||
'notification_purge.btn_none': 'Select\nnone',
|
'notification_purge.btn_none': 'Select\nnone',
|
||||||
'notification_purge.btn_invert': 'Invert\nselection',
|
'notification_purge.btn_invert': 'Invert\nselection',
|
||||||
|
@ -66,6 +172,25 @@ const messages = {
|
||||||
'advanced_options.threaded_mode.long': 'Automatically opens a reply on posting',
|
'advanced_options.threaded_mode.long': 'Automatically opens a reply on posting',
|
||||||
'advanced_options.threaded_mode.tooltip': 'Threaded mode enabled',
|
'advanced_options.threaded_mode.tooltip': 'Threaded mode enabled',
|
||||||
|
|
||||||
|
'endorsed_accounts_editor.endorsed_accounts': 'Featured accounts',
|
||||||
|
|
||||||
|
'account.add_account_note': 'Add note for @{name}',
|
||||||
|
'account_note.cancel': 'Cancel',
|
||||||
|
'account_note.save': 'Save',
|
||||||
|
'account_note.edit': 'Edit',
|
||||||
|
'account_note.glitch_placeholder': 'No comment provided',
|
||||||
|
'account.joined': 'Joined {date}',
|
||||||
|
'account.follows': 'Follows',
|
||||||
|
|
||||||
|
'home.column_settings.advanced': 'Advanced',
|
||||||
|
'home.column_settings.filter_regex': 'Filter out by regular expressions',
|
||||||
|
'direct.group_by_conversations': 'Group by conversation',
|
||||||
|
'community.column_settings.allow_local_only': 'Show local-only toots',
|
||||||
|
|
||||||
|
'keyboard_shortcuts.bookmark': 'to bookmark',
|
||||||
|
'keyboard_shortcuts.toggle_collapse': 'to collapse/uncollapse toots',
|
||||||
|
'keyboard_shortcuts.secondary_toot': 'to send toot using secondary privacy setting',
|
||||||
|
|
||||||
'tooltips.reactions': 'Reactions',
|
'tooltips.reactions': 'Reactions',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
206
app/javascript/flavours/glitch/locales/en.json
Normal file
206
app/javascript/flavours/glitch/locales/en.json
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
{
|
||||||
|
"about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
|
||||||
|
"account.add_account_note": "Add note for @{name}",
|
||||||
|
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
|
||||||
|
"account.follows": "Follows",
|
||||||
|
"account.joined": "Joined {date}",
|
||||||
|
"account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
|
||||||
|
"account.view_full_profile": "View full profile",
|
||||||
|
"account_note.cancel": "Cancel",
|
||||||
|
"account_note.edit": "Edit",
|
||||||
|
"account_note.glitch_placeholder": "No comment provided",
|
||||||
|
"account_note.save": "Save",
|
||||||
|
"advanced_options.icon_title": "Advanced options",
|
||||||
|
"advanced_options.local-only.long": "Do not post to other instances",
|
||||||
|
"advanced_options.local-only.short": "Local-only",
|
||||||
|
"advanced_options.local-only.tooltip": "This post is local-only",
|
||||||
|
"advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
|
||||||
|
"advanced_options.threaded_mode.short": "Threaded mode",
|
||||||
|
"advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
|
||||||
|
"boost_modal.missing_description": "This toot contains some media without description",
|
||||||
|
"column.favourited_by": "Favourited by",
|
||||||
|
"column.heading": "Misc",
|
||||||
|
"column.reblogged_by": "Boosted by",
|
||||||
|
"column.subheading": "Miscellaneous options",
|
||||||
|
"column_header.profile": "Profile",
|
||||||
|
"column_subheading.lists": "Lists",
|
||||||
|
"column_subheading.navigation": "Navigation",
|
||||||
|
"community.column_settings.allow_local_only": "Show local-only toots",
|
||||||
|
"compose.attach": "Attach...",
|
||||||
|
"compose.attach.doodle": "Draw something",
|
||||||
|
"compose.attach.upload": "Upload a file",
|
||||||
|
"compose.content-type.html": "HTML",
|
||||||
|
"compose.content-type.markdown": "Markdown",
|
||||||
|
"compose.content-type.plain": "Plain text",
|
||||||
|
"compose_form.poll.multiple_choices": "Allow multiple choices",
|
||||||
|
"compose_form.poll.single_choice": "Allow one choice",
|
||||||
|
"compose_form.spoiler": "Hide text behind warning",
|
||||||
|
"confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
|
||||||
|
"confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
|
||||||
|
"confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
|
||||||
|
"confirmations.missing_media_description.confirm": "Send anyway",
|
||||||
|
"confirmations.missing_media_description.edit": "Edit media",
|
||||||
|
"confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
|
||||||
|
"confirmations.unfilter.author": "Author",
|
||||||
|
"confirmations.unfilter.confirm": "Show",
|
||||||
|
"confirmations.unfilter.edit_filter": "Edit filter",
|
||||||
|
"confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
|
||||||
|
"content-type.change": "Content type",
|
||||||
|
"direct.group_by_conversations": "Group by conversation",
|
||||||
|
"endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
|
||||||
|
"favourite_modal.combo": "You can press {combo} to skip this next time",
|
||||||
|
"getting_started.onboarding": "Show me around",
|
||||||
|
"home.column_settings.advanced": "Advanced",
|
||||||
|
"home.column_settings.filter_regex": "Filter out by regular expressions",
|
||||||
|
"home.column_settings.show_direct": "Show DMs",
|
||||||
|
"home.settings": "Column settings",
|
||||||
|
"keyboard_shortcuts.bookmark": "to bookmark",
|
||||||
|
"keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
|
||||||
|
"keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
|
||||||
|
"tooltips.reactions": "Reactions",
|
||||||
|
"layout.auto": "Auto",
|
||||||
|
"layout.desktop": "Desktop",
|
||||||
|
"layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
|
||||||
|
"layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
|
||||||
|
"layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
|
||||||
|
"layout.single": "Mobile",
|
||||||
|
"media_gallery.sensitive": "Sensitive",
|
||||||
|
"moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
|
||||||
|
"navigation_bar.app_settings": "App settings",
|
||||||
|
"navigation_bar.featured_users": "Featured users",
|
||||||
|
"navigation_bar.info": "Extended information",
|
||||||
|
"navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
|
||||||
|
"navigation_bar.misc": "Misc",
|
||||||
|
"notification.markForDeletion": "Mark for deletion",
|
||||||
|
"notification.reaction": "{name} reacted to your post",
|
||||||
|
"notifications.column_settings.reaction": "Reactions:",
|
||||||
|
"notification_purge.btn_all": "Select\nall",
|
||||||
|
"notification_purge.btn_apply": "Clear\nselected",
|
||||||
|
"notification_purge.btn_invert": "Invert\nselection",
|
||||||
|
"notification_purge.btn_none": "Select\nnone",
|
||||||
|
"notification_purge.start": "Enter notification cleaning mode",
|
||||||
|
"notifications.marked_clear": "Clear selected notifications",
|
||||||
|
"notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?",
|
||||||
|
"onboarding.done": "Done",
|
||||||
|
"onboarding.next": "Next",
|
||||||
|
"onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
|
||||||
|
"onboarding.page_four.home": "The home timeline shows posts from people you follow.",
|
||||||
|
"onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
|
||||||
|
"onboarding.page_one.federation": "{domain} is an 'instance' of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
|
||||||
|
"onboarding.page_one.welcome": "Welcome to {domain}!",
|
||||||
|
"onboarding.page_six.admin": "Your instance's admin is {admin}.",
|
||||||
|
"onboarding.page_six.almost_done": "Almost done...",
|
||||||
|
"onboarding.page_six.appetoot": "Bon Appetoot!",
|
||||||
|
"onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"onboarding.page_six.guidelines": "community guidelines",
|
||||||
|
"onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
|
||||||
|
"onboarding.page_six.various_app": "mobile apps",
|
||||||
|
"onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
|
||||||
|
"onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
|
||||||
|
"onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
|
||||||
|
"onboarding.skip": "Skip",
|
||||||
|
"settings.always_show_spoilers_field": "Always enable the Content Warning field",
|
||||||
|
"settings.auto_collapse": "Automatic collapsing",
|
||||||
|
"settings.auto_collapse_all": "Everything",
|
||||||
|
"settings.auto_collapse_lengthy": "Lengthy toots",
|
||||||
|
"settings.auto_collapse_media": "Toots with media",
|
||||||
|
"settings.auto_collapse_notifications": "Notifications",
|
||||||
|
"settings.auto_collapse_reblogs": "Boosts",
|
||||||
|
"settings.auto_collapse_replies": "Replies",
|
||||||
|
"settings.close": "Close",
|
||||||
|
"settings.collapsed_statuses": "Collapsed toots",
|
||||||
|
"settings.compose_box_opts": "Compose box",
|
||||||
|
"settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
|
||||||
|
"settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
|
||||||
|
"settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
|
||||||
|
"settings.content_warnings": "Content Warnings",
|
||||||
|
"settings.content_warnings.regexp": "Regular expression",
|
||||||
|
"settings.content_warnings_filter": "Content warnings to not automatically unfold:",
|
||||||
|
"settings.content_warnings_media_outside": "Display media attachments outside content warnings",
|
||||||
|
"settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
|
||||||
|
"settings.content_warnings_shared_state": "Show/hide content of all copies at once",
|
||||||
|
"settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
|
||||||
|
"settings.content_warnings_unfold_opts": "Auto-unfolding options",
|
||||||
|
"settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
|
||||||
|
"settings.enable_collapsed": "Enable collapsed toots",
|
||||||
|
"settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
|
||||||
|
"settings.enter_amount_prompt": "Enter an amount",
|
||||||
|
"settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
|
||||||
|
"settings.general": "General",
|
||||||
|
"settings.hicolor_privacy_icons": "High color privacy icons",
|
||||||
|
"settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
|
||||||
|
"settings.image_backgrounds": "Image backgrounds",
|
||||||
|
"settings.image_backgrounds_media": "Preview collapsed toot media",
|
||||||
|
"settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
|
||||||
|
"settings.image_backgrounds_users": "Give collapsed toots an image background",
|
||||||
|
"settings.inline_preview_cards": "Inline preview cards for external links",
|
||||||
|
"settings.layout": "Layout:",
|
||||||
|
"settings.layout_opts": "Layout options",
|
||||||
|
"settings.media": "Media",
|
||||||
|
"settings.media_fullwidth": "Full-width media previews",
|
||||||
|
"settings.num_visible_reactions": "Number of visible reactions",
|
||||||
|
"settings.media_letterbox": "Letterbox media",
|
||||||
|
"settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
|
||||||
|
"settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
|
||||||
|
"settings.notifications.favicon_badge": "Unread notifications favicon badge",
|
||||||
|
"settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
|
||||||
|
"settings.notifications.tab_badge": "Unread notifications badge",
|
||||||
|
"settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
|
||||||
|
"settings.notifications_opts": "Notifications options",
|
||||||
|
"settings.pop_in_left": "Left",
|
||||||
|
"settings.pop_in_player": "Enable pop-in player",
|
||||||
|
"settings.pop_in_position": "Pop-in player position:",
|
||||||
|
"settings.pop_in_right": "Right",
|
||||||
|
"settings.preferences": "User preferences",
|
||||||
|
"settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
|
||||||
|
"settings.preselect_on_reply": "Pre-select usernames on reply",
|
||||||
|
"settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
|
||||||
|
"settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
|
||||||
|
"settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
|
||||||
|
"settings.rewrite_mentions_no": "Do not rewrite mentions",
|
||||||
|
"settings.rewrite_mentions_username": "Rewrite with username",
|
||||||
|
"settings.shared_settings_link": "user preferences",
|
||||||
|
"settings.show_action_bar": "Show action buttons in collapsed toots",
|
||||||
|
"settings.show_content_type_choice": "Show content-type choice when authoring toots",
|
||||||
|
"settings.show_reply_counter": "Display an estimate of the reply count",
|
||||||
|
"settings.side_arm": "Secondary toot button:",
|
||||||
|
"settings.side_arm.none": "None",
|
||||||
|
"settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
|
||||||
|
"settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
|
||||||
|
"settings.side_arm_reply_mode.keep": "Keep its set privacy",
|
||||||
|
"settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
|
||||||
|
"settings.status_icons": "Toot icons",
|
||||||
|
"settings.status_icons_language": "Language indicator",
|
||||||
|
"settings.status_icons_local_only": "Local-only indicator",
|
||||||
|
"settings.status_icons_media": "Media and poll indicators",
|
||||||
|
"settings.status_icons_reply": "Reply indicator",
|
||||||
|
"settings.status_icons_visibility": "Toot privacy indicator",
|
||||||
|
"settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
|
||||||
|
"settings.tag_misleading_links": "Tag misleading links",
|
||||||
|
"settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
|
||||||
|
"settings.wide_view": "Wide view (Desktop mode only)",
|
||||||
|
"settings.wide_view_hint": "Stretches columns to better fill the available space.",
|
||||||
|
"status.collapse": "Collapse",
|
||||||
|
"status.react": "React",
|
||||||
|
"status.has_audio": "Features attached audio files",
|
||||||
|
"status.has_pictures": "Features attached pictures",
|
||||||
|
"status.has_preview_card": "Features an attached preview card",
|
||||||
|
"status.has_video": "Features attached videos",
|
||||||
|
"status.in_reply_to": "This toot is a reply",
|
||||||
|
"status.is_poll": "This toot is a poll",
|
||||||
|
"status.local_only": "Only visible from your instance",
|
||||||
|
"status.sensitive_toggle": "Click to view",
|
||||||
|
"status.uncollapse": "Uncollapse",
|
||||||
|
"web_app_crash.change_your_settings": "Change your {settings}",
|
||||||
|
"web_app_crash.content": "You could try any of the following:",
|
||||||
|
"web_app_crash.debug_info": "Debug information",
|
||||||
|
"web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
|
||||||
|
"web_app_crash.issue_tracker": "issue tracker",
|
||||||
|
"web_app_crash.reload": "Reload",
|
||||||
|
"web_app_crash.reload_page": "{reload} the current page",
|
||||||
|
"web_app_crash.report_issue": "Report a bug in the {issuetracker}",
|
||||||
|
"web_app_crash.settings": "settings",
|
||||||
|
"web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/eo.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
52
app/javascript/flavours/glitch/locales/eo.json
Normal file
52
app/javascript/flavours/glitch/locales/eo.json
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"account.add_account_note": "Aldoni noton por @{name}",
|
||||||
|
"account_note.cancel": "Nuligi",
|
||||||
|
"account_note.edit": "Redakti",
|
||||||
|
"account_note.save": "Konservi",
|
||||||
|
"column.reblogged_by": "Diskonigita de",
|
||||||
|
"column.subheading": "Diversaj agordoj",
|
||||||
|
"column_header.profile": "Profilo",
|
||||||
|
"column_subheading.lists": "Listoj",
|
||||||
|
"compose.attach": "Aldoni…",
|
||||||
|
"compose.attach.doodle": "Desegni ion",
|
||||||
|
"compose.attach.upload": "Alŝuti dosieron",
|
||||||
|
"compose.content-type.html": "HTML",
|
||||||
|
"compose.content-type.markdown": "Markdown",
|
||||||
|
"confirmations.unfilter.author": "Aŭtoro",
|
||||||
|
"confirmations.unfilter.confirm": "Montri",
|
||||||
|
"confirmations.unfilter.edit_filter": "Redakti filtrilon",
|
||||||
|
"navigation_bar.keyboard_shortcuts": "Fulmoklavoj",
|
||||||
|
"notification_purge.btn_all": "Selekti ĉiujn",
|
||||||
|
"notification_purge.btn_apply": "Forigi selektajn",
|
||||||
|
"notification_purge.btn_invert": "Inverti selekton",
|
||||||
|
"notification_purge.btn_none": "Elekti neniun",
|
||||||
|
"notifications.marked_clear": "Forigi selektajn sciigojn",
|
||||||
|
"onboarding.next": "Sekva",
|
||||||
|
"onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||||
|
"onboarding.page_six.almost_done": "Preskaŭ finita…",
|
||||||
|
"onboarding.page_six.apps_available": "Estas {apps} disponeblaj por iOS, Android kaj aliaj sistemoj.",
|
||||||
|
"onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||||
|
"onboarding.page_six.various_app": "poŝtelefonaj aplikaĵoj",
|
||||||
|
"settings.auto_collapse_all": "Ĉiuj",
|
||||||
|
"settings.auto_collapse_lengthy": "Longaj afiŝoj",
|
||||||
|
"settings.auto_collapse_media": "Afiŝoj kun aŭdovidaĵoj",
|
||||||
|
"settings.auto_collapse_notifications": "Sciigoj",
|
||||||
|
"settings.auto_collapse_reblogs": "Diskonigoj",
|
||||||
|
"settings.auto_collapse_replies": "Respondoj",
|
||||||
|
"settings.close": "Fermi",
|
||||||
|
"settings.content_warnings": "Content warnings",
|
||||||
|
"settings.content_warnings.regexp": "Regula esprimo",
|
||||||
|
"settings.preferences": "Preferences",
|
||||||
|
"settings.shared_settings_link": "preferoj de uzanto",
|
||||||
|
"settings.side_arm": "Duaranga butono por afiŝi:",
|
||||||
|
"settings.side_arm.none": "Neniu",
|
||||||
|
"settings.status_icons": "Ikonoj sur la afiŝoj",
|
||||||
|
"settings.status_icons_language": "Indikilo de lingvo",
|
||||||
|
"settings.status_icons_media": "Indikilo de aŭdovidaĵojn kaj balotenketo",
|
||||||
|
"settings.status_icons_reply": "Indikilo de respondoj",
|
||||||
|
"settings.status_icons_visibility": "Indikilo de privateco de afiŝo",
|
||||||
|
"web_app_crash.change_your_settings": "Ŝanĝi viajn {settings}",
|
||||||
|
"web_app_crash.reload": "Reŝarĝi",
|
||||||
|
"web_app_crash.reload_page": "{reload} la nunan paĝon",
|
||||||
|
"web_app_crash.settings": "agordojn"
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
import inherited from 'mastodon/locales/es-AR.json';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
// No translations available.
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Object.assign({}, inherited, messages);
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue