Merge remote-tracking branch 'upstream/main'
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
commit
f251c8ea47
112 changed files with 2176 additions and 1039 deletions
42
.github/workflows/build-releases.yml
vendored
42
.github/workflows/build-releases.yml
vendored
|
|
@ -9,7 +9,44 @@ permissions:
|
|||
packages: write
|
||||
|
||||
jobs:
|
||||
check-latest-stable:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
latest: ${{ steps.check.outputs.is_latest_stable }}
|
||||
steps:
|
||||
# Repository needs to be cloned to list branches
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check latest stable
|
||||
shell: bash
|
||||
id: check
|
||||
run: |
|
||||
ref="${GITHUB_REF#refs/tags/}"
|
||||
|
||||
if [[ "$ref" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?$ ]]; then
|
||||
current="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
|
||||
else
|
||||
echo "tag $ref is not semver"
|
||||
echo "is_latest_stable=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
latest=$(git for-each-ref --format='%(refname:short)' "refs/remotes/origin/stable-*.*" \
|
||||
| sed -E 's#^origin/stable-##' \
|
||||
| sort -Vr \
|
||||
| head -n1)
|
||||
|
||||
if [[ "$current" == "$latest" ]]; then
|
||||
echo "is_latest_stable=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_latest_stable=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build-image:
|
||||
needs: check-latest-stable
|
||||
uses: ./.github/workflows/build-container-image.yml
|
||||
with:
|
||||
file_to_build: Dockerfile
|
||||
|
|
@ -21,13 +58,14 @@ jobs:
|
|||
# Only tag with latest when ran against the latest stable branch
|
||||
# This needs to be updated after each minor version release
|
||||
flavor: |
|
||||
latest=${{ startsWith(github.ref, 'refs/tags/v4.5.') }}
|
||||
latest=${{ needs.check-latest-stable.outputs.latest }}
|
||||
tags: |
|
||||
type=pep440,pattern={{raw}}
|
||||
type=pep440,pattern=v{{major}}.{{minor}}
|
||||
secrets: inherit
|
||||
|
||||
build-image-streaming:
|
||||
needs: check-latest-stable
|
||||
uses: ./.github/workflows/build-container-image.yml
|
||||
with:
|
||||
file_to_build: streaming/Dockerfile
|
||||
|
|
@ -39,7 +77,7 @@ jobs:
|
|||
# Only tag with latest when ran against the latest stable branch
|
||||
# This needs to be updated after each minor version release
|
||||
flavor: |
|
||||
latest=${{ startsWith(github.ref, 'refs/tags/v4.5.') }}
|
||||
latest=${{ needs.check-latest-stable.outputs.latest }}
|
||||
tags: |
|
||||
type=pep440,pattern={{raw}}
|
||||
type=pep440,pattern=v{{major}}.{{minor}}
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
diff --git a/lib/index.js b/lib/index.js
|
||||
index 16ed6be8be8f555cc99096c2ff60954b42dc313d..d009c069770d066ad0db7ad02de1ea473a29334e 100644
|
||||
--- a/lib/index.js
|
||||
+++ b/lib/index.js
|
||||
@@ -99,7 +99,7 @@ function lodash(_ref) {
|
||||
|
||||
var node = _ref3;
|
||||
|
||||
- if ((0, _types.isModuleDeclaration)(node)) {
|
||||
+ if ((0, _types.isImportDeclaration)(node) || (0, _types.isExportDeclaration)(node)) {
|
||||
isModule = true;
|
||||
break;
|
||||
}
|
||||
20
CHANGELOG.md
20
CHANGELOG.md
|
|
@ -2,6 +2,26 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [4.5.3] - 2025-12-08
|
||||
|
||||
### Security
|
||||
|
||||
- Fix inconsistent error handling leaking information on existence of private posts ([GHSA-gwhw-gcjx-72v8](https://github.com/mastodon/mastodon/security/advisories/GHSA-gwhw-gcjx-72v8))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix “Delete and Redraft” on a non-quote being treated as a quote post in some cases (#37140 by @ClearlyClaire)
|
||||
- Fix YouTube embeds by sending referer (#37126 by @ChaosExAnima)
|
||||
- Fix streamed quoted polls not being hydrated correctly (#37118 by @ClearlyClaire)
|
||||
- Fix creation of duplicate conversations (#37108 by @oneiros)
|
||||
- Fix extraneous `noreferrer` in external links (#37107 by @ChaosExAnima)
|
||||
- Fix edge case error handling in some database migrations (#37079 by @ClearlyClaire)
|
||||
- Fix error handling when re-fetching already-known statuses (#37077 by @ClearlyClaire)
|
||||
- Fix post navigation in single-column mode when Advanced UI is enabled (#37044 by @diondiondion)
|
||||
- Fix `tootctl status remove` removing quoted posts and remote quotes of local posts (#37009 by @ClearlyClaire)
|
||||
- Fix known expensive S3 batch delete operation failing because of short timeouts (#37004 by @ClearlyClaire)
|
||||
- Fix compose autosuggest always lowercasing input token (#36995 by @ClearlyClaire)
|
||||
|
||||
## [4.5.2] - 2025-11-20
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
10
Gemfile.lock
10
Gemfile.lock
|
|
@ -304,8 +304,8 @@ GEM
|
|||
highline (3.1.2)
|
||||
reline
|
||||
hiredis (0.6.3)
|
||||
hiredis-client (0.26.1)
|
||||
redis-client (= 0.26.1)
|
||||
hiredis-client (0.26.2)
|
||||
redis-client (= 0.26.2)
|
||||
hkdf (0.3.0)
|
||||
htmlentities (4.3.4)
|
||||
http (5.3.1)
|
||||
|
|
@ -469,7 +469,7 @@ GEM
|
|||
nokogiri (1.18.10)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
oj (3.16.12)
|
||||
oj (3.16.13)
|
||||
bigdecimal (>= 3.0)
|
||||
ostruct (>= 0.2)
|
||||
omniauth (2.1.4)
|
||||
|
|
@ -703,7 +703,7 @@ GEM
|
|||
reline
|
||||
redcarpet (3.6.1)
|
||||
redis (4.8.1)
|
||||
redis-client (0.26.1)
|
||||
redis-client (0.26.2)
|
||||
connection_pool
|
||||
regexp_parser (2.11.3)
|
||||
reline (0.6.3)
|
||||
|
|
@ -855,7 +855,7 @@ GEM
|
|||
unicode-display_width (>= 1.1.1, < 4)
|
||||
terrapin (1.1.1)
|
||||
climate_control
|
||||
test-prof (1.4.4)
|
||||
test-prof (1.5.0)
|
||||
thor (1.4.0)
|
||||
tilt (2.6.1)
|
||||
timeout (0.4.3)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ActivityPub::LikesController < ActivityPub::BaseController
|
|||
def set_status
|
||||
@status = @account.statuses.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class ActivityPub::QuoteAuthorizationsController < ActivityPub::BaseController
|
|||
return not_found unless @quote.status.present? && @quote.quoted_status.present?
|
||||
|
||||
authorize @quote.quoted_status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
def set_status
|
||||
@status = @account.statuses.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ActivityPub::SharesController < ActivityPub::BaseController
|
|||
def set_status
|
||||
@status = @account.statuses.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
|
|||
def set_poll
|
||||
@poll = Poll.find(params[:poll_id])
|
||||
authorize @poll.status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class Api::V1::PollsController < Api::BaseController
|
|||
def set_poll
|
||||
@poll = Poll.find(params[:id])
|
||||
authorize @poll.status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class Api::V1::Statuses::BaseController < Api::BaseController
|
|||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class Api::V1::Statuses::BookmarksController < Api::V1::Statuses::BaseController
|
|||
bookmark&.destroy!
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, bookmarks_map: { @status.id => false })
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class Api::V1::Statuses::FavouritesController < Api::V1::Statuses::BaseControlle
|
|||
|
||||
relationships = StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }, attributes_map: { @status.id => { favourites_count: count } })
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: relationships
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class Api::V1::Statuses::ReblogsController < Api::V1::Statuses::BaseController
|
|||
|
||||
relationships = StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }, attributes_map: { @reblog.id => { reblogs_count: count } })
|
||||
render json: @reblog, serializer: REST::StatusSerializer, relationships: relationships
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ class Api::V1::Statuses::ReblogsController < Api::V1::Statuses::BaseController
|
|||
def set_reblog
|
||||
@reblog = Status.find(params[:status_id])
|
||||
authorize @reblog, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
def set_status
|
||||
@status = Status.find(params[:id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,20 +3,34 @@
|
|||
class Api::V1Alpha::CollectionsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
DEFAULT_COLLECTIONS_LIMIT = 40
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||
render json: { error: ValidationErrorFormatter.new(e).as_json }, status: 422
|
||||
end
|
||||
|
||||
before_action :check_feature_enabled
|
||||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:collections' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:collections' }, only: [:create, :update, :destroy]
|
||||
|
||||
before_action :require_user!, only: [:create, :update, :destroy]
|
||||
|
||||
before_action :set_account, only: [:index]
|
||||
before_action :set_collections, only: [:index]
|
||||
before_action :set_collection, only: [:show, :update, :destroy]
|
||||
|
||||
after_action :insert_pagination_headers, only: [:index]
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
def index
|
||||
cache_if_unauthenticated!
|
||||
authorize Collection, :index?
|
||||
|
||||
render json: @collections, each_serializer: REST::BaseCollectionSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
authorize @collection, :show?
|
||||
|
|
@ -50,6 +64,18 @@ class Api::V1Alpha::CollectionsController < Api::BaseController
|
|||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def set_collections
|
||||
@collections = @account.collections
|
||||
.with_tag
|
||||
.order(created_at: :desc)
|
||||
.offset(offset_param)
|
||||
.limit(limit_param(DEFAULT_COLLECTIONS_LIMIT))
|
||||
end
|
||||
|
||||
def set_collection
|
||||
@collection = Collection.find(params[:id])
|
||||
end
|
||||
|
|
@ -65,4 +91,24 @@ class Api::V1Alpha::CollectionsController < Api::BaseController
|
|||
def check_feature_enabled
|
||||
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
|
||||
end
|
||||
|
||||
def next_path
|
||||
return unless records_continue?
|
||||
|
||||
api_v1_alpha_account_collections_url(@account, pagination_params(offset: offset_param + limit_param(DEFAULT_COLLECTIONS_LIMIT)))
|
||||
end
|
||||
|
||||
def prev_path
|
||||
return if offset_param.zero?
|
||||
|
||||
api_v1_alpha_account_collections_url(@account, pagination_params(offset: offset_param - limit_param(DEFAULT_COLLECTIONS_LIMIT)))
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
((offset_param * limit_param(DEFAULT_COLLECTIONS_LIMIT)) + @collections.size) < @account.collections.size
|
||||
end
|
||||
|
||||
def offset_param
|
||||
params[:offset].to_i
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class Api::Web::EmbedsController < Api::Web::BaseController
|
|||
def set_status
|
||||
@status = Status.find(params[:id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class AuthorizeInteractionsController < ApplicationController
|
|||
def set_resource
|
||||
@resource = located_resource
|
||||
authorize(@resource, :show?) if @resource.is_a?(Status)
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class MediaController < ApplicationController
|
|||
|
||||
def verify_permitted_status!
|
||||
authorize @media_attachment.status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class StatusesController < ApplicationController
|
|||
def set_status
|
||||
@status = @account.statuses.find(params[:id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ module ApplicationHelper
|
|||
|
||||
def html_classes
|
||||
output = []
|
||||
output << content_for(:html_classes)
|
||||
output << 'system-font' if current_account&.user&.setting_system_font_ui
|
||||
output << 'custom-scrollbars' unless current_account&.user&.setting_system_scrollbars_ui
|
||||
output << (current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion')
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ import { createRoot } from 'react-dom/client';
|
|||
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
|
||||
import {
|
||||
importFetchedAccounts,
|
||||
importFetchedStatuses,
|
||||
} from '@/mastodon/actions/importer';
|
||||
import { importFetchedStatuses } from '@/mastodon/actions/importer';
|
||||
import { hydrateStore } from '@/mastodon/actions/store';
|
||||
import type { ApiAnnualReportResponse } from '@/mastodon/api/annual_report';
|
||||
import { Router } from '@/mastodon/components/router';
|
||||
import { WrapstodonShare } from '@/mastodon/features/annual_report/share';
|
||||
import { WrapstodonSharedPage } from '@/mastodon/features/annual_report/shared_page';
|
||||
import { IntlProvider, loadLocale } from '@/mastodon/locales';
|
||||
import { loadPolyfills } from '@/mastodon/polyfills';
|
||||
import ready from '@/mastodon/ready';
|
||||
|
|
@ -33,7 +31,14 @@ function loaded() {
|
|||
if (!report) {
|
||||
throw new Error('Initial state report not found');
|
||||
}
|
||||
store.dispatch(importFetchedAccounts(initialState.accounts));
|
||||
|
||||
// Set up store
|
||||
store.dispatch(
|
||||
hydrateStore({
|
||||
meta: { locale: document.documentElement.lang },
|
||||
accounts: initialState.accounts,
|
||||
}),
|
||||
);
|
||||
store.dispatch(importFetchedStatuses(initialState.statuses));
|
||||
|
||||
store.dispatch(setReport(report));
|
||||
|
|
@ -43,7 +48,7 @@ function loaded() {
|
|||
<IntlProvider>
|
||||
<ReduxProvider store={store}>
|
||||
<Router>
|
||||
<WrapstodonShare />
|
||||
<WrapstodonSharedPage />
|
||||
</Router>
|
||||
</ReduxProvider>
|
||||
</IntlProvider>,
|
||||
|
|
|
|||
100
app/javascript/fonts/silkscreen-wrapstodon/OFL.txt
Normal file
100
app/javascript/fonts/silkscreen-wrapstodon/OFL.txt
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
Below you'll find the original License file for the Silkscreen font.
|
||||
The file used on Mastodon is a custom file subset to only include the
|
||||
characters "Wrapstodon 0123456789" using the Font Squirrel Font-face Generator
|
||||
(https://www.fontsquirrel.com/tools/webfont-generator)
|
||||
|
||||
-----------------------------------------------------------
|
||||
|
||||
Copyright 2001 The Silkscreen Project Authors (https://github.com/googlefonts/silkscreen)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
BIN
app/javascript/images/archetypes/space_elements.png
Normal file
BIN
app/javascript/images/archetypes/space_elements.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -22,6 +22,8 @@ export function hydrateStore(rawState) {
|
|||
|
||||
dispatch(hydrateCompose());
|
||||
dispatch(hydrateSearch());
|
||||
if (rawState.accounts) {
|
||||
dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import { reinsertAnnualReport, TIMELINE_WRAPSTODON } from '@/mastodon/reducers/slices/annual_report';
|
||||
import api, { getLinks } from 'mastodon/api';
|
||||
import { compareId } from 'mastodon/compare_id';
|
||||
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
||||
|
||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
import { submitMarkers } from './markers';
|
||||
import {timelineDelete} from './timelines_typed';
|
||||
import { timelineDelete } from './timelines_typed';
|
||||
|
||||
export { disconnectTimeline } from './timelines_typed';
|
||||
|
||||
|
|
@ -24,9 +25,16 @@ export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
|
|||
export const TIMELINE_MARK_AS_PARTIAL = 'TIMELINE_MARK_AS_PARTIAL';
|
||||
export const TIMELINE_INSERT = 'TIMELINE_INSERT';
|
||||
|
||||
// When adding new special markers here, make sure to update TIMELINE_NON_STATUS_MARKERS in actions/timelines_typed.js
|
||||
export const TIMELINE_SUGGESTIONS = 'inline-follow-suggestions';
|
||||
export const TIMELINE_GAP = null;
|
||||
|
||||
export const TIMELINE_NON_STATUS_MARKERS = [
|
||||
TIMELINE_GAP,
|
||||
TIMELINE_SUGGESTIONS,
|
||||
TIMELINE_WRAPSTODON,
|
||||
];
|
||||
|
||||
export const loadPending = timeline => ({
|
||||
type: TIMELINE_LOAD_PENDING,
|
||||
timeline,
|
||||
|
|
@ -124,6 +132,7 @@ export function expandTimeline(timelineId, path, params = {}) {
|
|||
|
||||
if (timelineId === 'home') {
|
||||
dispatch(submitMarkers());
|
||||
dispatch(reinsertAnnualReport())
|
||||
}
|
||||
} catch(error) {
|
||||
dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@ import { createAction } from '@reduxjs/toolkit';
|
|||
|
||||
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
||||
|
||||
import { TIMELINE_NON_STATUS_MARKERS } from './timelines';
|
||||
|
||||
export function isNonStatusId(value: unknown) {
|
||||
return TIMELINE_NON_STATUS_MARKERS.includes(value as string | null);
|
||||
}
|
||||
|
||||
export const disconnectTimeline = createAction(
|
||||
'timeline/disconnect',
|
||||
({ timeline }: { timeline: string }) => ({
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ class Status extends ImmutablePureComponent {
|
|||
hidden: PropTypes.bool,
|
||||
unread: PropTypes.bool,
|
||||
showThread: PropTypes.bool,
|
||||
showActions: PropTypes.bool,
|
||||
isQuotedPost: PropTypes.bool,
|
||||
shouldHighlightOnMount: PropTypes.bool,
|
||||
getScrollPosition: PropTypes.func,
|
||||
|
|
@ -381,7 +382,7 @@ class Status extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { intl, hidden, featured, unfocusable, unread, showThread, isQuotedPost = false, scrollKey, pictureInPicture, previousId, nextInReplyToId, rootId, skipPrepend, avatarSize = 46, children } = this.props;
|
||||
const { intl, hidden, featured, unfocusable, unread, showThread, showActions = true, isQuotedPost = false, scrollKey, pictureInPicture, previousId, nextInReplyToId, rootId, skipPrepend, avatarSize = 46, children } = this.props;
|
||||
|
||||
let { status, account, ...other } = this.props;
|
||||
|
||||
|
|
@ -618,7 +619,7 @@ class Status extends ImmutablePureComponent {
|
|||
</>
|
||||
)}
|
||||
|
||||
{!isQuotedPost &&
|
||||
{(showActions && !isQuotedPost) &&
|
||||
<StatusActionBar scrollKey={scrollKey} status={status} account={account} {...other} />
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Button } from '@/mastodon/components/button';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
|
@ -12,7 +14,7 @@ export const AnnualReportAnnouncement: React.FC<{
|
|||
onOpen: () => void;
|
||||
}> = ({ year, hasData, isLoading, onRequestBuild, onOpen }) => {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={classNames('theme-dark', styles.wrapper)}>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id='annual_report.announcement.title'
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@
|
|||
text-align: center;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text-on-media);
|
||||
background: var(--color-bg-media-base);
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-bg-primary);
|
||||
background:
|
||||
radial-gradient(at 40% 87%, #240c9a99 0, transparent 50%),
|
||||
radial-gradient(at 19% 10%, #6b0c9a99 0, transparent 50%),
|
||||
radial-gradient(at 90% 27%, #9a0c8299 0, transparent 50%),
|
||||
radial-gradient(at 16% 95%, #1e948299 0, transparent 50%)
|
||||
var(--color-bg-media-base);
|
||||
var(--color-bg-primary);
|
||||
border-bottom: 1px solid var(--color-border-primary);
|
||||
|
||||
h2 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
|
||||
import {
|
||||
accountFactoryState,
|
||||
annualReportFactory,
|
||||
statusFactoryState,
|
||||
} from '@/testing/factories';
|
||||
|
||||
import { AnnualReport } from '.';
|
||||
|
||||
const SAMPLE_HASHTAG = {
|
||||
name: 'Mastodon',
|
||||
count: 14,
|
||||
};
|
||||
|
||||
const meta = {
|
||||
title: 'Components/AnnualReport',
|
||||
component: AnnualReport,
|
||||
args: {
|
||||
context: 'standalone',
|
||||
},
|
||||
parameters: {
|
||||
state: {
|
||||
accounts: {
|
||||
'1': accountFactoryState({ display_name: 'Freddie Fruitbat' }),
|
||||
},
|
||||
statuses: {
|
||||
'1': statusFactoryState(),
|
||||
},
|
||||
annualReport: annualReportFactory({
|
||||
top_hashtag: SAMPLE_HASHTAG,
|
||||
}),
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof AnnualReport>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Standalone: Story = {
|
||||
args: {
|
||||
context: 'standalone',
|
||||
},
|
||||
};
|
||||
|
||||
export const InModal: Story = {
|
||||
args: {
|
||||
context: 'modal',
|
||||
},
|
||||
};
|
||||
|
||||
export const ArchetypeOracle: Story = {
|
||||
...InModal,
|
||||
parameters: {
|
||||
state: {
|
||||
annualReport: annualReportFactory({
|
||||
archetype: 'oracle',
|
||||
top_hashtag: SAMPLE_HASHTAG,
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const NoHashtag: Story = {
|
||||
...InModal,
|
||||
parameters: {
|
||||
state: {
|
||||
annualReport: annualReportFactory({
|
||||
archetype: 'booster',
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const NoNewPosts: Story = {
|
||||
...InModal,
|
||||
parameters: {
|
||||
state: {
|
||||
annualReport: annualReportFactory({
|
||||
archetype: 'pollster',
|
||||
top_hashtag: SAMPLE_HASHTAG,
|
||||
without_posts: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const NoNewPostsNoHashtag: Story = {
|
||||
...InModal,
|
||||
parameters: {
|
||||
state: {
|
||||
annualReport: annualReportFactory({
|
||||
archetype: 'replier',
|
||||
without_posts: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,65 +1,214 @@
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import booster from '@/images/archetypes/booster.png';
|
||||
import lurker from '@/images/archetypes/lurker.png';
|
||||
import oracle from '@/images/archetypes/oracle.png';
|
||||
import pollster from '@/images/archetypes/pollster.png';
|
||||
import replier from '@/images/archetypes/replier.png';
|
||||
import type { Archetype as ArchetypeData } from '@/mastodon/models/annual_report';
|
||||
import space_elements from '@/images/archetypes/space_elements.png';
|
||||
import { Avatar } from '@/mastodon/components/avatar';
|
||||
import { Button } from '@/mastodon/components/button';
|
||||
import type { Account } from '@/mastodon/models/account';
|
||||
import type {
|
||||
AnnualReport,
|
||||
Archetype as ArchetypeData,
|
||||
} from '@/mastodon/models/annual_report';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
import { ShareButton } from './share_button';
|
||||
|
||||
export const archetypeNames = defineMessages<ArchetypeData>({
|
||||
booster: {
|
||||
id: 'annual_report.summary.archetype.booster',
|
||||
defaultMessage: 'The cool-hunter',
|
||||
id: 'annual_report.summary.archetype.booster.name',
|
||||
defaultMessage: 'The Archer',
|
||||
},
|
||||
replier: {
|
||||
id: 'annual_report.summary.archetype.replier',
|
||||
defaultMessage: 'The social butterfly',
|
||||
id: 'annual_report.summary.archetype.replier.name',
|
||||
defaultMessage: 'The Butterfly',
|
||||
},
|
||||
pollster: {
|
||||
id: 'annual_report.summary.archetype.pollster',
|
||||
defaultMessage: 'The pollster',
|
||||
id: 'annual_report.summary.archetype.pollster.name',
|
||||
defaultMessage: 'The Wonderer',
|
||||
},
|
||||
lurker: {
|
||||
id: 'annual_report.summary.archetype.lurker',
|
||||
defaultMessage: 'The lurker',
|
||||
id: 'annual_report.summary.archetype.lurker.name',
|
||||
defaultMessage: 'The Stoic',
|
||||
},
|
||||
oracle: {
|
||||
id: 'annual_report.summary.archetype.oracle',
|
||||
defaultMessage: 'The oracle',
|
||||
id: 'annual_report.summary.archetype.oracle.name',
|
||||
defaultMessage: 'The Oracle',
|
||||
},
|
||||
});
|
||||
|
||||
export const Archetype: React.FC<{
|
||||
data: ArchetypeData;
|
||||
}> = ({ data }) => {
|
||||
const intl = useIntl();
|
||||
let illustration;
|
||||
export const archetypeSelfDescriptions = defineMessages<ArchetypeData>({
|
||||
booster: {
|
||||
id: 'annual_report.summary.archetype.booster.desc_self',
|
||||
defaultMessage:
|
||||
'You stayed on the hunt for posts to boost, amplifying other creators with perfect aim.',
|
||||
},
|
||||
replier: {
|
||||
id: 'annual_report.summary.archetype.replier.desc_self',
|
||||
defaultMessage:
|
||||
'You frequently replied to other people’s posts, pollinating Mastodon with new discussions.',
|
||||
},
|
||||
pollster: {
|
||||
id: 'annual_report.summary.archetype.pollster.desc_self',
|
||||
defaultMessage:
|
||||
'You created more polls than other post types, cultivating curiosity on Mastodon.',
|
||||
},
|
||||
lurker: {
|
||||
id: 'annual_report.summary.archetype.lurker.desc_self',
|
||||
defaultMessage:
|
||||
'We know you were out there, somewhere, enjoying Mastodon in your own quiet way.',
|
||||
},
|
||||
oracle: {
|
||||
id: 'annual_report.summary.archetype.oracle.desc_self',
|
||||
defaultMessage:
|
||||
'You created new posts more than replies, keeping Mastodon fresh and future-facing.',
|
||||
},
|
||||
});
|
||||
|
||||
switch (data) {
|
||||
case 'booster':
|
||||
illustration = booster;
|
||||
break;
|
||||
case 'replier':
|
||||
illustration = replier;
|
||||
break;
|
||||
case 'pollster':
|
||||
illustration = pollster;
|
||||
break;
|
||||
case 'lurker':
|
||||
illustration = lurker;
|
||||
break;
|
||||
case 'oracle':
|
||||
illustration = oracle;
|
||||
break;
|
||||
}
|
||||
export const archetypePublicDescriptions = defineMessages<ArchetypeData>({
|
||||
booster: {
|
||||
id: 'annual_report.summary.archetype.booster.desc_public',
|
||||
defaultMessage:
|
||||
'{name} stayed on the hunt for posts to boost, amplifying other creators with perfect aim.',
|
||||
},
|
||||
replier: {
|
||||
id: 'annual_report.summary.archetype.replier.desc_public',
|
||||
defaultMessage:
|
||||
'{name} frequently replied to other people’s posts, pollinating Mastodon with new discussions.',
|
||||
},
|
||||
pollster: {
|
||||
id: 'annual_report.summary.archetype.pollster.desc_public',
|
||||
defaultMessage:
|
||||
'{name} created more polls than other post types, cultivating curiosity on Mastodon.',
|
||||
},
|
||||
lurker: {
|
||||
id: 'annual_report.summary.archetype.lurker.desc_public',
|
||||
defaultMessage:
|
||||
'We know {name} was out there, somewhere, enjoying Mastodon in their own quiet way.',
|
||||
},
|
||||
oracle: {
|
||||
id: 'annual_report.summary.archetype.oracle.desc_public',
|
||||
defaultMessage:
|
||||
'{name} created new posts more than replies, keeping Mastodon fresh and future-facing.',
|
||||
},
|
||||
});
|
||||
|
||||
const illustrations = {
|
||||
booster,
|
||||
replier,
|
||||
pollster,
|
||||
lurker,
|
||||
oracle,
|
||||
} as const;
|
||||
|
||||
export const Archetype: React.FC<{
|
||||
report: AnnualReport;
|
||||
account?: Account;
|
||||
context: 'modal' | 'standalone';
|
||||
}> = ({ report, account, context }) => {
|
||||
const intl = useIntl();
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const isSelfView = context === 'modal';
|
||||
|
||||
const [isRevealed, setIsRevealed] = useState(!isSelfView);
|
||||
const reveal = useCallback(() => {
|
||||
setIsRevealed(true);
|
||||
wrapperRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const archetype = report.data.archetype;
|
||||
const descriptions = isSelfView
|
||||
? archetypeSelfDescriptions
|
||||
: archetypePublicDescriptions;
|
||||
|
||||
const name = account?.display_name;
|
||||
|
||||
return (
|
||||
<div className='annual-report__bento__box annual-report__summary__archetype'>
|
||||
<div className='annual-report__summary__archetype__label'>
|
||||
{intl.formatMessage(archetypeNames[data])}
|
||||
<div
|
||||
className={classNames(styles.box, styles.archetype)}
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
tabIndex={0}
|
||||
ref={wrapperRef}
|
||||
>
|
||||
<div className={styles.archetypeArtboard}>
|
||||
{account && (
|
||||
<Avatar
|
||||
account={account}
|
||||
size={50}
|
||||
className={styles.archetypeAvatar}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.archetypeIllustrationWrapper}>
|
||||
<img
|
||||
src={illustrations[archetype]}
|
||||
alt=''
|
||||
className={classNames(
|
||||
styles.archetypeIllustration,
|
||||
isRevealed ? '' : styles.blurredImage,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<img src={illustration} alt='' />
|
||||
<img
|
||||
src={space_elements}
|
||||
alt=''
|
||||
className={styles.archetypePlanetRing}
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames(styles.content, styles.comfortable)}>
|
||||
<h2 className={styles.title}>
|
||||
{isSelfView ? (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.archetype.title_self'
|
||||
defaultMessage='Your archetype'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.archetype.title_public'
|
||||
defaultMessage="{name}'s archetype"
|
||||
values={{ name }}
|
||||
/>
|
||||
)}
|
||||
</h2>
|
||||
<p className={styles.statLarge}>
|
||||
{isRevealed ? (
|
||||
intl.formatMessage(archetypeNames[archetype])
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.archetype.die_drei_fragezeichen'
|
||||
defaultMessage='???'
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{isRevealed ? (
|
||||
intl.formatMessage(descriptions[archetype], {
|
||||
name,
|
||||
})
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.archetype.reveal_description'
|
||||
defaultMessage='Thanks for being part of Mastodon! Time to find out which archetype you embodied in {year}.'
|
||||
values={{ year: report.year }}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
{!isRevealed && (
|
||||
<Button onClick={reveal}>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.archetype.reveal'
|
||||
defaultMessage='Reveal my archetype'
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
{isRevealed && isSelfView && <ShareButton report={report} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,68 +1,24 @@
|
|||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
||||
|
||||
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
import type { TimeSeriesMonth } from 'mastodon/models/annual_report';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const Followers: React.FC<{
|
||||
data: TimeSeriesMonth[];
|
||||
total?: number;
|
||||
}> = ({ data, total }) => {
|
||||
const change = data.reduce((sum, item) => sum + item.followers, 0);
|
||||
|
||||
const cumulativeGraph = data.reduce(
|
||||
(newData, item) => [
|
||||
...newData,
|
||||
item.followers + (newData[newData.length - 1] ?? 0),
|
||||
],
|
||||
[0],
|
||||
);
|
||||
|
||||
count: number;
|
||||
}> = ({ count }) => {
|
||||
return (
|
||||
<div className='annual-report__bento__box annual-report__summary__followers'>
|
||||
<Sparklines data={cumulativeGraph} margin={0}>
|
||||
<svg>
|
||||
<defs>
|
||||
<linearGradient id='gradient' x1='0%' y1='0%' x2='0%' y2='100%'>
|
||||
<stop
|
||||
offset='0%'
|
||||
stopColor='var(--sparkline-gradient-top)'
|
||||
stopOpacity='1'
|
||||
/>
|
||||
<stop
|
||||
offset='100%'
|
||||
stopColor='var(--sparkline-gradient-bottom)'
|
||||
stopOpacity='0'
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<SparklinesCurve style={{ fill: 'none' }} />
|
||||
</Sparklines>
|
||||
|
||||
<div className='annual-report__summary__followers__foreground'>
|
||||
<div className='annual-report__summary__followers__number'>
|
||||
{change > -1 ? '+' : '-'}
|
||||
<FormattedNumber value={change} />
|
||||
<div className={classNames(styles.box, styles.followers, styles.content)}>
|
||||
<div className={styles.statLarge}>
|
||||
<FormattedNumber value={count} />
|
||||
</div>
|
||||
|
||||
<div className='annual-report__summary__followers__label'>
|
||||
<span>
|
||||
<div className={styles.title}>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.followers.followers'
|
||||
defaultMessage='followers'
|
||||
id='annual_report.summary.followers.new_followers'
|
||||
defaultMessage='{count, plural, one {new follower} other {new followers}}'
|
||||
values={{ count }}
|
||||
/>
|
||||
</span>
|
||||
<div className='annual-report__summary__followers__footnote'>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.followers.total'
|
||||
defaultMessage='{count} total'
|
||||
values={{ count: <ShortNumber value={total ?? 0} /> }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,102 +1,78 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-return,
|
||||
@typescript-eslint/no-explicit-any,
|
||||
@typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import { useCallback } from 'react';
|
||||
@typescript-eslint/no-unsafe-assignment,
|
||||
@typescript-eslint/no-unsafe-member-access,
|
||||
@typescript-eslint/no-unsafe-call */
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { DisplayName } from '@/mastodon/components/display_name';
|
||||
import { toggleStatusSpoilers } from 'mastodon/actions/statuses';
|
||||
import { DetailedStatus } from 'mastodon/features/status/components/detailed_status';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
||||
import type { TopStatuses } from 'mastodon/models/annual_report';
|
||||
import { makeGetStatus, makeGetPictureInPicture } from 'mastodon/selectors';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
import { makeGetStatus } from 'mastodon/selectors';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const getStatus = makeGetStatus() as unknown as (arg0: any, arg1: any) => any;
|
||||
const getPictureInPicture = makeGetPictureInPicture() as unknown as (
|
||||
arg0: any,
|
||||
arg1: any,
|
||||
) => any;
|
||||
|
||||
export const HighlightedPost: React.FC<{
|
||||
data: TopStatuses;
|
||||
}> = ({ data }) => {
|
||||
let statusId, label;
|
||||
context: 'modal' | 'standalone';
|
||||
}> = ({ data, context }) => {
|
||||
const { by_reblogs, by_favourites, by_replies } = data;
|
||||
|
||||
if (data.by_reblogs) {
|
||||
statusId = data.by_reblogs;
|
||||
label = (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.by_reblogs'
|
||||
defaultMessage='most boosted post'
|
||||
/>
|
||||
);
|
||||
} else if (data.by_favourites) {
|
||||
statusId = data.by_favourites;
|
||||
label = (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.by_favourites'
|
||||
defaultMessage='most favourited post'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
statusId = data.by_replies;
|
||||
label = (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.by_replies'
|
||||
defaultMessage='post with the most replies'
|
||||
/>
|
||||
);
|
||||
}
|
||||
const statusId = by_reblogs || by_favourites || by_replies;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const domain = useAppSelector((state) => state.meta.get('domain'));
|
||||
const status = useAppSelector((state) =>
|
||||
statusId ? getStatus(state, { id: statusId }) : undefined,
|
||||
);
|
||||
const pictureInPicture = useAppSelector((state) =>
|
||||
statusId ? getPictureInPicture(state, { id: statusId }) : undefined,
|
||||
);
|
||||
const account = useAppSelector((state) =>
|
||||
me ? state.accounts.get(me) : undefined,
|
||||
);
|
||||
|
||||
const handleToggleHidden = useCallback(() => {
|
||||
dispatch(toggleStatusSpoilers(statusId));
|
||||
}, [dispatch, statusId]);
|
||||
|
||||
if (!status) {
|
||||
return (
|
||||
<div className='annual-report__bento__box annual-report__summary__most-boosted-post' />
|
||||
return <div className={classNames(styles.box, styles.mostBoostedPost)} />;
|
||||
}
|
||||
|
||||
let label;
|
||||
if (by_reblogs) {
|
||||
label = (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.boost_count'
|
||||
defaultMessage='This post was boosted {count, plural, one {once} other {# times}}.'
|
||||
values={{ count: status.get('reblogs_count') }}
|
||||
/>
|
||||
);
|
||||
} else if (by_favourites) {
|
||||
label = (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.favourite_count'
|
||||
defaultMessage='This post was favorited {count, plural, one {once} other {# times}}.'
|
||||
values={{ count: status.get('favourites_count') }}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
label = (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.reply_count'
|
||||
defaultMessage='This post got {count, plural, one {one reply} other {# replies}}.'
|
||||
values={{ count: status.get('replies_count') }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const displayName = (
|
||||
<span className='display-name'>
|
||||
<strong className='display-name__html'>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.possessive'
|
||||
defaultMessage="{name}'s"
|
||||
values={{
|
||||
name: <DisplayName account={account} variant='simple' />,
|
||||
}}
|
||||
/>
|
||||
</strong>
|
||||
<span className='display-name__account'>{label}</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='annual-report__bento__box annual-report__summary__most-boosted-post'>
|
||||
<DetailedStatus
|
||||
status={status}
|
||||
pictureInPicture={pictureInPicture}
|
||||
domain={domain}
|
||||
onToggleHidden={handleToggleHidden}
|
||||
overrideDisplayName={displayName}
|
||||
<div className={classNames(styles.box, styles.mostBoostedPost)}>
|
||||
<div className={styles.content}>
|
||||
<h2 className={styles.title}>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.highlighted_post.title'
|
||||
defaultMessage='Most popular post'
|
||||
/>
|
||||
</h2>
|
||||
{context === 'modal' && <p>{label}</p>}
|
||||
</div>
|
||||
|
||||
<StatusQuoteManager showActions={false} id={statusId} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
318
app/javascript/mastodon/features/annual_report/index.module.scss
Normal file
318
app/javascript/mastodon/features/annual_report/index.module.scss
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
$mobile-breakpoint: 540px;
|
||||
|
||||
@font-face {
|
||||
font-family: silkscreen-wrapstodon;
|
||||
src: url('@/fonts/silkscreen-wrapstodon/silkscreen-regular.woff2')
|
||||
format('woff2');
|
||||
font-weight: normal;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.modalWrapper {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 40px;
|
||||
overflow-y: auto;
|
||||
pointer-events: none;
|
||||
scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary);
|
||||
|
||||
@media (width < $mobile-breakpoint) {
|
||||
padding-inline: 10px;
|
||||
}
|
||||
|
||||
.loading-indicator .circular-progress {
|
||||
color: var(--lime);
|
||||
}
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
--default-icon-color: var(--color-bg-primary);
|
||||
--default-bg-color: var(--color-text-primary);
|
||||
--hover-icon-color: var(--color-bg-primary);
|
||||
--hover-bg-color: var(--color-text-primary);
|
||||
--corner-distance: 18px;
|
||||
|
||||
position: absolute;
|
||||
top: var(--corner-distance);
|
||||
right: var(--corner-distance);
|
||||
padding: 8px;
|
||||
border-radius: 100%;
|
||||
|
||||
@media (width < $mobile-breakpoint) {
|
||||
--corner-distance: 16px;
|
||||
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
max-width: 600px;
|
||||
padding: 24px;
|
||||
contain: layout;
|
||||
flex: 0 0 auto;
|
||||
pointer-events: auto;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-bg-primary);
|
||||
background:
|
||||
radial-gradient(at 40% 87%, #240c9a99 0, transparent 50%),
|
||||
radial-gradient(at 19% 10%, #6b0c9a99 0, transparent 50%),
|
||||
radial-gradient(at 90% 27%, #9a0c8299 0, transparent 50%),
|
||||
radial-gradient(at 16% 95%, #1e948299 0, transparent 50%),
|
||||
radial-gradient(at 80% 91%, #16dae499 0, transparent 50%)
|
||||
var(--color-bg-primary);
|
||||
border-radius: 40px;
|
||||
|
||||
@media (width < $mobile-breakpoint) {
|
||||
padding-inline: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-radius: 28px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
background: inherit;
|
||||
border-radius: inherit;
|
||||
filter: blur(20px);
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 18px;
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
font-family: silkscreen-wrapstodon, monospace;
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
margin-bottom: 8px;
|
||||
padding-inline: 40px; // Prevent overlap with close button
|
||||
|
||||
@media (width < $mobile-breakpoint) {
|
||||
font-size: 22px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.stack {
|
||||
--grid-spacing: 12px;
|
||||
|
||||
display: grid;
|
||||
gap: var(--grid-spacing);
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
background: rgb(from var(--color-bg-primary) r g b / 60%);
|
||||
box-shadow: inset 0 0 0 1px rgb(from var(--color-text-primary) r g b / 40%);
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-inline: 0;
|
||||
display: block;
|
||||
height: 1px;
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
transparent,
|
||||
white,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
&::before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
text-wrap: balance;
|
||||
|
||||
&.comfortable {
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
text-transform: uppercase;
|
||||
color: #c2c8ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.statLarge {
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.statExtraLarge {
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
@media (width < $mobile-breakpoint) {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.mostBoostedPost {
|
||||
padding: 0;
|
||||
padding-top: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.statsGrid {
|
||||
display: grid;
|
||||
gap: var(--grid-spacing);
|
||||
grid-template-columns: 1fr 2fr;
|
||||
grid-template-areas:
|
||||
'followers hashtag'
|
||||
'new-posts hashtag';
|
||||
|
||||
@media (width < $mobile-breakpoint) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-areas:
|
||||
'followers new-posts'
|
||||
'hashtag hashtag';
|
||||
}
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.onlyHashtag {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: 'hashtag';
|
||||
}
|
||||
|
||||
&.noHashtag {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-areas: 'followers new-posts';
|
||||
}
|
||||
|
||||
&.singleNumber {
|
||||
grid-template-columns: 1fr 2fr;
|
||||
grid-template-areas: 'number hashtag';
|
||||
|
||||
@media (width < $mobile-breakpoint) {
|
||||
grid-template-areas:
|
||||
'number number'
|
||||
'hashtag hashtag';
|
||||
}
|
||||
}
|
||||
|
||||
&.singleNumber.noHashtag {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: 'number';
|
||||
}
|
||||
}
|
||||
|
||||
.followers {
|
||||
grid-area: followers;
|
||||
|
||||
.singleNumber & {
|
||||
grid-area: number;
|
||||
}
|
||||
}
|
||||
|
||||
.newPosts {
|
||||
grid-area: new-posts;
|
||||
|
||||
.singleNumber & {
|
||||
grid-area: number;
|
||||
}
|
||||
}
|
||||
|
||||
.mostUsedHashtag {
|
||||
grid-area: hashtag;
|
||||
padding-block: 24px;
|
||||
}
|
||||
|
||||
.archetype {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
p {
|
||||
max-width: 460px;
|
||||
}
|
||||
}
|
||||
|
||||
.archetypeArtboard {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
width: 180px;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.archetypeAvatar {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: 4px;
|
||||
border-radius: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.archetypeIllustrationWrapper {
|
||||
position: relative;
|
||||
width: 92px;
|
||||
aspect-ratio: 1;
|
||||
overflow: hidden;
|
||||
border-radius: 100%;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
box-shadow: inset -10px -4px 15px #00000080;
|
||||
}
|
||||
}
|
||||
|
||||
.archetypeIllustration {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.blurredImage {
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.archetypePlanetRing {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
|
@ -1,95 +1,117 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { defineMessage, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { defineMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { useLocation } from 'react-router';
|
||||
|
||||
import classNames from 'classnames/bind';
|
||||
|
||||
import { focusCompose, resetCompose } from '@/mastodon/actions/compose';
|
||||
import { closeModal } from '@/mastodon/actions/modal';
|
||||
import { Button } from '@/mastodon/components/button';
|
||||
import { IconButton } from '@/mastodon/components/icon_button';
|
||||
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
|
||||
import { me } from '@/mastodon/initial_state';
|
||||
import type { AnnualReport as AnnualReportData } from '@/mastodon/models/annual_report';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
|
||||
import { Archetype, archetypeNames } from './archetype';
|
||||
import { Archetype } from './archetype';
|
||||
import { Followers } from './followers';
|
||||
import { HighlightedPost } from './highlighted_post';
|
||||
import styles from './index.module.scss';
|
||||
import { MostUsedHashtag } from './most_used_hashtag';
|
||||
import { NewPosts } from './new_posts';
|
||||
|
||||
const shareMessage = defineMessage({
|
||||
const moduleClassNames = classNames.bind(styles);
|
||||
|
||||
export const shareMessage = defineMessage({
|
||||
id: 'annual_report.summary.share_message',
|
||||
defaultMessage: 'I got the {archetype} archetype!',
|
||||
});
|
||||
|
||||
// Share = false when using the embedded version of the report.
|
||||
export const AnnualReport: FC<{ share?: boolean }> = ({ share = true }) => {
|
||||
const currentAccount = useAppSelector((state) =>
|
||||
me ? state.accounts.get(me) : undefined,
|
||||
);
|
||||
export const AnnualReport: FC<{ context?: 'modal' | 'standalone' }> = ({
|
||||
context = 'standalone',
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const report = useAppSelector((state) => state.annualReport.report);
|
||||
const account = useAppSelector((state) => {
|
||||
if (me) {
|
||||
return state.accounts.get(me);
|
||||
}
|
||||
if (report?.schema_version === 2) {
|
||||
return state.accounts.get(report.account_id);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const close = useCallback(() => {
|
||||
dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }));
|
||||
}, [dispatch]);
|
||||
|
||||
// Close modal when navigating away from within
|
||||
const { pathname } = useLocation();
|
||||
const [initialPathname] = useState(pathname);
|
||||
useEffect(() => {
|
||||
if (pathname !== initialPathname) {
|
||||
close();
|
||||
}
|
||||
}, [pathname, initialPathname, close]);
|
||||
|
||||
if (!report) {
|
||||
return <LoadingIndicator />;
|
||||
}
|
||||
|
||||
const newPostCount = report.data.time_series.reduce(
|
||||
(sum, item) => sum + item.statuses,
|
||||
0,
|
||||
);
|
||||
|
||||
const newFollowerCount =
|
||||
context === 'modal' &&
|
||||
report.data.time_series.reduce((sum, item) => sum + item.followers, 0);
|
||||
|
||||
const topHashtag = report.data.top_hashtags[0];
|
||||
|
||||
return (
|
||||
<div className='annual-report'>
|
||||
<div className='annual-report__header'>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.thanks'
|
||||
defaultMessage='Thanks for being part of Mastodon!'
|
||||
<div className={moduleClassNames(styles.wrapper, 'theme-dark')}>
|
||||
<div className={styles.header}>
|
||||
<h1>Wrapstodon {report.year}</h1>
|
||||
{account && <p>@{account.acct}</p>}
|
||||
{context === 'modal' && (
|
||||
<IconButton
|
||||
title={intl.formatMessage({
|
||||
id: 'annual_report.summary.close',
|
||||
defaultMessage: 'Close',
|
||||
})}
|
||||
className={styles.closeButton}
|
||||
icon='close'
|
||||
iconComponent={CloseIcon}
|
||||
onClick={close}
|
||||
/>
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.here_it_is'
|
||||
defaultMessage='Here is your {year} in review:'
|
||||
values={{ year: report.year }}
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='annual-report__bento annual-report__summary'>
|
||||
<Archetype data={report.data.archetype} />
|
||||
<HighlightedPost data={report.data.top_statuses} />
|
||||
<Followers
|
||||
data={report.data.time_series}
|
||||
total={currentAccount?.followers_count}
|
||||
<div className={styles.stack}>
|
||||
<HighlightedPost data={report.data.top_statuses} context={context} />
|
||||
<div
|
||||
className={moduleClassNames(styles.statsGrid, {
|
||||
noHashtag: !topHashtag,
|
||||
onlyHashtag: !(newFollowerCount && newPostCount),
|
||||
singleNumber: !!newFollowerCount !== !!newPostCount,
|
||||
})}
|
||||
>
|
||||
{!!newFollowerCount && <Followers count={newFollowerCount} />}
|
||||
{!!newPostCount && <NewPosts count={newPostCount} />}
|
||||
{topHashtag && (
|
||||
<MostUsedHashtag
|
||||
hashtag={topHashtag}
|
||||
name={account?.display_name}
|
||||
context={context}
|
||||
/>
|
||||
<MostUsedHashtag data={report.data.top_hashtags} />
|
||||
<NewPosts data={report.data.time_series} />
|
||||
{share && <ShareButton report={report} />}
|
||||
)}
|
||||
</div>
|
||||
<Archetype report={report} account={account} context={context} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ShareButton: FC<{ report: AnnualReportData }> = ({ report }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const handleShareClick = useCallback(() => {
|
||||
// Generate the share message.
|
||||
const archetypeName = intl.formatMessage(
|
||||
archetypeNames[report.data.archetype],
|
||||
);
|
||||
const shareLines = [
|
||||
intl.formatMessage(shareMessage, {
|
||||
archetype: archetypeName,
|
||||
}),
|
||||
];
|
||||
// Share URL is only available for schema version 2.
|
||||
if (report.schema_version === 2 && report.share_url) {
|
||||
shareLines.push(report.share_url);
|
||||
}
|
||||
shareLines.push(`#Wrapstodon${report.year}`);
|
||||
|
||||
// Reset the composer and focus it with the share message, then close the modal.
|
||||
dispatch(resetCompose());
|
||||
dispatch(focusCompose(shareLines.join('\n\n')));
|
||||
dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }));
|
||||
}, [report, intl, dispatch]);
|
||||
|
||||
return <Button text='Share here' onClick={handleShareClick} />;
|
||||
};
|
||||
|
|
|
|||
29
app/javascript/mastodon/features/annual_report/modal.tsx
Normal file
29
app/javascript/mastodon/features/annual_report/modal.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { AnnualReport } from '.';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const AnnualReportModal: React.FC<{
|
||||
onChangeBackgroundColor: (color: string) => void;
|
||||
}> = ({ onChangeBackgroundColor }) => {
|
||||
useEffect(() => {
|
||||
onChangeBackgroundColor('var(--color-bg-media-base)');
|
||||
}, [onChangeBackgroundColor]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'modal-root__modal',
|
||||
styles.modalWrapper,
|
||||
'theme-dark',
|
||||
)}
|
||||
>
|
||||
<AnnualReport context='modal' />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default AnnualReportModal;
|
||||
|
|
@ -1,30 +1,46 @@
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { NameAndCount } from 'mastodon/models/annual_report';
|
||||
|
||||
export const MostUsedHashtag: React.FC<{
|
||||
data: NameAndCount[];
|
||||
}> = ({ data }) => {
|
||||
const hashtag = data[0];
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const MostUsedHashtag: React.FC<{
|
||||
hashtag: NameAndCount;
|
||||
name: string | undefined;
|
||||
context: 'modal' | 'standalone';
|
||||
}> = ({ hashtag, name, context }) => {
|
||||
return (
|
||||
<div className='annual-report__bento__box annual-report__summary__most-used-hashtag'>
|
||||
<div className='annual-report__summary__most-used-hashtag__hashtag'>
|
||||
{hashtag ? (
|
||||
<>#{hashtag.name}</>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.most_used_hashtag.none'
|
||||
defaultMessage='None'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className='annual-report__summary__most-used-hashtag__label'>
|
||||
<div
|
||||
className={classNames(styles.box, styles.mostUsedHashtag, styles.content)}
|
||||
>
|
||||
<div className={styles.title}>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.most_used_hashtag.most_used_hashtag'
|
||||
defaultMessage='most used hashtag'
|
||||
defaultMessage='Most used hashtag'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.statExtraLarge}>#{hashtag.name}</div>
|
||||
|
||||
<p>
|
||||
{context === 'modal' ? (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.most_used_hashtag.used_count'
|
||||
defaultMessage='You included this hashtag in {count, plural, one {one post} other {# posts}}.'
|
||||
values={{ count: hashtag.count }}
|
||||
/>
|
||||
) : (
|
||||
name && (
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.most_used_hashtag.used_count_public'
|
||||
defaultMessage='{name} included this hashtag in {count, plural, one {one post} other {# posts}}.'
|
||||
values={{ count: hashtag.count, name }}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,51 +1,23 @@
|
|||
import { FormattedNumber, FormattedMessage } from 'react-intl';
|
||||
|
||||
import ChatBubbleIcon from '@/material-icons/400-24px/chat_bubble.svg?react';
|
||||
import type { TimeSeriesMonth } from 'mastodon/models/annual_report';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
export const NewPosts: React.FC<{
|
||||
data: TimeSeriesMonth[];
|
||||
}> = ({ data }) => {
|
||||
const posts = data.reduce((sum, item) => sum + item.statuses, 0);
|
||||
|
||||
count: number;
|
||||
}> = ({ count }) => {
|
||||
return (
|
||||
<div className='annual-report__bento__box annual-report__summary__new-posts'>
|
||||
<svg width={500} height={500}>
|
||||
<defs>
|
||||
<pattern
|
||||
id='posts'
|
||||
x='0'
|
||||
y='0'
|
||||
width='32'
|
||||
height='35'
|
||||
patternUnits='userSpaceOnUse'
|
||||
>
|
||||
<circle cx='12' cy='12' r='12' fill='var(--lime)' />
|
||||
<ChatBubbleIcon
|
||||
fill='var(--indigo-1)'
|
||||
x='4'
|
||||
y='4'
|
||||
width='16'
|
||||
height='16'
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<rect
|
||||
width={500}
|
||||
height={500}
|
||||
fill='url(#posts)'
|
||||
style={{ opacity: 0.2 }}
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<div className='annual-report__summary__new-posts__number'>
|
||||
<FormattedNumber value={posts} />
|
||||
<div className={classNames(styles.box, styles.newPosts, styles.content)}>
|
||||
<div className={styles.statLarge}>
|
||||
<FormattedNumber value={count} />
|
||||
</div>
|
||||
<div className='annual-report__summary__new-posts__label'>
|
||||
|
||||
<div className={styles.title}>
|
||||
<FormattedMessage
|
||||
id='annual_report.summary.new_posts.new_posts'
|
||||
defaultMessage='new posts'
|
||||
defaultMessage='{count, plural, one {new post} other {new posts}}'
|
||||
values={{ count }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import type { FC } from 'react';
|
||||
|
||||
import { IconLogo } from '@/mastodon/components/logo';
|
||||
|
||||
import { AnnualReport } from './index';
|
||||
import classes from './share.module.css';
|
||||
|
||||
export const WrapstodonShare: FC = () => {
|
||||
return (
|
||||
<main className={classes.wrapper}>
|
||||
<AnnualReport share={false} />
|
||||
<footer className={classes.footer}>
|
||||
<IconLogo className={classes.logo} />
|
||||
Generated with ♥ by the Mastodon team
|
||||
</footer>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { useCallback } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { resetCompose, focusCompose } from '@/mastodon/actions/compose';
|
||||
import { closeModal } from '@/mastodon/actions/modal';
|
||||
import { Button } from '@/mastodon/components/button';
|
||||
import type { AnnualReport as AnnualReportData } from '@/mastodon/models/annual_report';
|
||||
import { useAppDispatch } from '@/mastodon/store';
|
||||
|
||||
import { shareMessage } from '.';
|
||||
import { archetypeNames } from './archetype';
|
||||
|
||||
export const ShareButton: FC<{ report: AnnualReportData }> = ({ report }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const handleShareClick = useCallback(() => {
|
||||
// Generate the share message.
|
||||
const archetypeName = intl.formatMessage(
|
||||
archetypeNames[report.data.archetype],
|
||||
);
|
||||
const shareLines = [
|
||||
intl.formatMessage(shareMessage, {
|
||||
archetype: archetypeName,
|
||||
}),
|
||||
];
|
||||
// Share URL is only available for schema version 2.
|
||||
if (report.schema_version === 2 && report.share_url) {
|
||||
shareLines.push(report.share_url);
|
||||
}
|
||||
shareLines.push(`#Wrapstodon${report.year}`);
|
||||
|
||||
// Reset the composer and focus it with the share message, then close the modal.
|
||||
dispatch(resetCompose());
|
||||
dispatch(focusCompose(shareLines.join('\n\n')));
|
||||
dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }));
|
||||
}, [report, intl, dispatch]);
|
||||
|
||||
return <Button text='Share here' onClick={handleShareClick} />;
|
||||
};
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
.wrapper {
|
||||
max-width: 40rem;
|
||||
margin: 0 auto;
|
||||
max-width: max-content;
|
||||
margin-inline: auto;
|
||||
padding: 40px 10px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 1rem;
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import type { FC } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { IconLogo } from '@/mastodon/components/logo';
|
||||
|
||||
import { AnnualReport } from './index';
|
||||
import classes from './shared_page.module.css';
|
||||
|
||||
export const WrapstodonSharedPage: FC = () => {
|
||||
return (
|
||||
<main className={classes.wrapper}>
|
||||
<AnnualReport />
|
||||
<footer className={classes.footer}>
|
||||
<IconLogo className={classes.logo} />
|
||||
<FormattedMessage
|
||||
id='annual_report.shared_page.footer'
|
||||
defaultMessage='Generated with {heart} by the Mastodon team'
|
||||
values={{ heart: '♥' }}
|
||||
/>
|
||||
</footer>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { AnnualReport } from 'mastodon/features/annual_report';
|
||||
|
||||
const AnnualReportModal: React.FC<{
|
||||
onChangeBackgroundColor: (color: string) => void;
|
||||
}> = ({ onChangeBackgroundColor }) => {
|
||||
useEffect(() => {
|
||||
onChangeBackgroundColor('var(--indigo-1)');
|
||||
}, [onChangeBackgroundColor]);
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal annual-report-modal'>
|
||||
<AnnualReport />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default AnnualReportModal;
|
||||
|
|
@ -4,10 +4,10 @@ import { connect } from 'react-redux';
|
|||
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { scrollTopTimeline, loadPending, TIMELINE_SUGGESTIONS } from '@/mastodon/actions/timelines';
|
||||
import { scrollTopTimeline, loadPending } from '@/mastodon/actions/timelines';
|
||||
import { isNonStatusId } from '@/mastodon/actions/timelines_typed';
|
||||
import StatusList from '@/mastodon/components/status_list';
|
||||
import { me } from '@/mastodon/initial_state';
|
||||
import { TIMELINE_WRAPSTODON } from '@/mastodon/reducers/slices/annual_report';
|
||||
|
||||
const makeGetStatusIds = (pending = false) => createSelector([
|
||||
(state, { type }) => state.getIn(['settings', type], ImmutableMap()),
|
||||
|
|
@ -15,7 +15,7 @@ const makeGetStatusIds = (pending = false) => createSelector([
|
|||
(state) => state.get('statuses'),
|
||||
], (columnSettings, statusIds, statuses) => {
|
||||
return statusIds.filter(id => {
|
||||
if (id === null || id === TIMELINE_SUGGESTIONS || id === TIMELINE_WRAPSTODON) return true;
|
||||
if (isNonStatusId(id)) return true;
|
||||
|
||||
const statusForId = statuses.get(id);
|
||||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ export function LinkTimeline () {
|
|||
}
|
||||
|
||||
export function AnnualReportModal () {
|
||||
return import('../components/annual_report_modal');
|
||||
return import('../../annual_report/modal');
|
||||
}
|
||||
|
||||
export function ListEdit () {
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@
|
|||
"alt_text_modal.describe_for_people_with_visual_impairments": "Popište to pro osoby se zrakovým postižením…",
|
||||
"alt_text_modal.done": "Hotovo",
|
||||
"announcement.announcement": "Oznámení",
|
||||
"annual_report.announcement.action_build": "Sestavit můj Wrapstodon",
|
||||
"annual_report.announcement.action_view": "Zobrazit můj Wrapstodon",
|
||||
"annual_report.announcement.description": "Zjistěte více o vaší aktivitě na Mastodonu za poslední rok.",
|
||||
"annual_report.announcement.title": "Je zde Wrapstodon {year}",
|
||||
"annual_report.summary.archetype.booster": "Lovec obsahu",
|
||||
"annual_report.summary.archetype.lurker": "Špión",
|
||||
"annual_report.summary.archetype.oracle": "Vědma",
|
||||
|
|
@ -131,6 +135,7 @@
|
|||
"annual_report.summary.new_posts.new_posts": "nové příspěvky",
|
||||
"annual_report.summary.percentile.text": "<topLabel>To vás umisťuje do horních</topLabel><percentage></percentage><bottomLabel> uživatelů domény {domain}.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "To, že jste zdejší smetánka, zůstane mezi námi ;).",
|
||||
"annual_report.summary.share_message": "Mám archetyp {archetype}!",
|
||||
"annual_report.summary.thanks": "Děkujeme, že jste součástí Mastodonu!",
|
||||
"attachments_list.unprocessed": "(nezpracováno)",
|
||||
"audio.hide": "Skrýt zvuk",
|
||||
|
|
@ -516,6 +521,7 @@
|
|||
"keyboard_shortcuts.toggle_hidden": "Zobrazit/skrýt text za varováním o obsahu",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Zobrazit/skrýt média",
|
||||
"keyboard_shortcuts.toot": "Začít nový příspěvek",
|
||||
"keyboard_shortcuts.top": "Přesunout na začátek seznamu",
|
||||
"keyboard_shortcuts.translate": "k přeložení příspěvku",
|
||||
"keyboard_shortcuts.unfocus": "Zrušit zaměření na nový příspěvek/hledání",
|
||||
"keyboard_shortcuts.up": "Posunout v seznamu nahoru",
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@
|
|||
"account.open_original_page": "Ursprüngliche Seite öffnen",
|
||||
"account.posts": "Beiträge",
|
||||
"account.posts_with_replies": "Beiträge & Antworten",
|
||||
"account.remove_from_followers": "{name} als Follower entfernen",
|
||||
"account.remove_from_followers": "@{name} als Follower entfernen",
|
||||
"account.report": "@{name} melden",
|
||||
"account.requested_follow": "{name} möchte dir folgen",
|
||||
"account.requests_to_follow_you": "Möchte dir folgen",
|
||||
|
|
@ -320,7 +320,7 @@
|
|||
"domain_pill.your_server": "Deine digitale Heimat. Hier „leben“ alle Beiträge von dir. Falls es dir hier nicht gefällt, kannst du jederzeit den Server wechseln und ebenso deine Follower übertragen.",
|
||||
"domain_pill.your_username": "Deine eindeutige Identität auf diesem Server. Es ist möglich, Profile mit dem gleichen Profilnamen auf verschiedenen Servern zu finden.",
|
||||
"dropdown.empty": "Option auswählen",
|
||||
"embed.instructions": "Du kannst diesen Beitrag auf deiner Website einbetten, indem du den nachfolgenden Code kopierst.",
|
||||
"embed.instructions": "Du kannst diesen Beitrag außerhalb des Fediverse (z. B. in deine Website) einbetten, indem du diesen Code kopierst und dort einfügst.",
|
||||
"embed.preview": "Vorschau:",
|
||||
"emoji_button.activity": "Aktivitäten",
|
||||
"emoji_button.clear": "Leeren",
|
||||
|
|
@ -370,8 +370,8 @@
|
|||
"errors.unexpected_crash.copy_stacktrace": "Fehlerdiagnose in die Zwischenablage kopieren",
|
||||
"errors.unexpected_crash.report_issue": "Fehler melden",
|
||||
"explore.suggested_follows": "Profile",
|
||||
"explore.title": "Angesagt",
|
||||
"explore.trending_links": "Neuigkeiten",
|
||||
"explore.title": "Im Trend",
|
||||
"explore.trending_links": "Artikel",
|
||||
"explore.trending_statuses": "Beiträge",
|
||||
"explore.trending_tags": "Hashtags",
|
||||
"featured_carousel.current": "<sr>Beitrag</sr> {current, number}/{max, number}",
|
||||
|
|
@ -414,15 +414,15 @@
|
|||
"follow_suggestions.personalized_suggestion": "Persönliche Empfehlung",
|
||||
"follow_suggestions.popular_suggestion": "Beliebte Empfehlung",
|
||||
"follow_suggestions.popular_suggestion_longer": "Beliebt auf {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Ähnlich zu Profilen, denen du seit kurzem folgst",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Ähnelt deinen kürzlich gefolgten Profilen",
|
||||
"follow_suggestions.view_all": "Alle anzeigen",
|
||||
"follow_suggestions.who_to_follow": "Empfohlene Profile",
|
||||
"follow_suggestions.who_to_follow": "Wem folgen?",
|
||||
"followed_tags": "Abonnierte Hashtags",
|
||||
"footer.about": "Über",
|
||||
"footer.about_this_server": "Über",
|
||||
"footer.directory": "Profilverzeichnis",
|
||||
"footer.get_app": "App herunterladen",
|
||||
"footer.keyboard_shortcuts": "Tastenkombinationen",
|
||||
"footer.keyboard_shortcuts": "Tastaturkürzel",
|
||||
"footer.privacy_policy": "Datenschutzerklärung",
|
||||
"footer.source_code": "Quellcode anzeigen",
|
||||
"footer.status": "Status",
|
||||
|
|
@ -483,48 +483,48 @@
|
|||
"interaction_modal.on_another_server": "Auf anderem Server",
|
||||
"interaction_modal.on_this_server": "Auf diesem Server",
|
||||
"interaction_modal.title": "Melde dich an, um fortzufahren",
|
||||
"interaction_modal.username_prompt": "z. B. {example}",
|
||||
"interaction_modal.username_prompt": "Z. B. {example}",
|
||||
"intervals.full.days": "{number, plural, one {# Tag} other {# Tage}}",
|
||||
"intervals.full.hours": "{number, plural, one {# Stunde} other {# Stunden}}",
|
||||
"intervals.full.minutes": "{number, plural, one {# Minute} other {# Minuten}}",
|
||||
"keyboard_shortcuts.back": "Zurücknavigieren",
|
||||
"keyboard_shortcuts.blocked": "Liste blockierter Profile öffnen",
|
||||
"keyboard_shortcuts.blocked": "Blockierte Profile öffnen",
|
||||
"keyboard_shortcuts.boost": "Beitrag teilen",
|
||||
"keyboard_shortcuts.column": "Auf die aktuelle Spalte fokussieren",
|
||||
"keyboard_shortcuts.column": "Aktuelle Spalte fokussieren",
|
||||
"keyboard_shortcuts.compose": "Eingabefeld fokussieren",
|
||||
"keyboard_shortcuts.description": "Beschreibung",
|
||||
"keyboard_shortcuts.direct": "Private Erwähnungen öffnen",
|
||||
"keyboard_shortcuts.down": "Ansicht nach unten bewegen",
|
||||
"keyboard_shortcuts.down": "Auswahl nach unten bewegen",
|
||||
"keyboard_shortcuts.enter": "Beitrag öffnen",
|
||||
"keyboard_shortcuts.favourite": "Beitrag favorisieren",
|
||||
"keyboard_shortcuts.favourites": "Favoriten öffnen",
|
||||
"keyboard_shortcuts.federated": "Föderierte Timeline öffnen",
|
||||
"keyboard_shortcuts.heading": "Tastenkombinationen",
|
||||
"keyboard_shortcuts.heading": "Tastenkürzel",
|
||||
"keyboard_shortcuts.home": "Startseite öffnen",
|
||||
"keyboard_shortcuts.hotkey": "Tastenkürzel",
|
||||
"keyboard_shortcuts.legend": "Tastenkombinationen anzeigen",
|
||||
"keyboard_shortcuts.legend": "Tastenkürzel anzeigen (diese Seite)",
|
||||
"keyboard_shortcuts.load_more": "Schaltfläche „Mehr laden“ fokussieren",
|
||||
"keyboard_shortcuts.local": "Lokale Timeline öffnen",
|
||||
"keyboard_shortcuts.mention": "Profil erwähnen",
|
||||
"keyboard_shortcuts.muted": "Liste stummgeschalteter Profile öffnen",
|
||||
"keyboard_shortcuts.my_profile": "Eigenes Profil aufrufen",
|
||||
"keyboard_shortcuts.notifications": "Benachrichtigungen aufrufen",
|
||||
"keyboard_shortcuts.open_media": "Medieninhalt öffnen",
|
||||
"keyboard_shortcuts.muted": "Stummgeschaltete Profile öffnen",
|
||||
"keyboard_shortcuts.my_profile": "Eigenes Profil öffnen",
|
||||
"keyboard_shortcuts.notifications": "Benachrichtigungen öffnen",
|
||||
"keyboard_shortcuts.open_media": "Medien öffnen",
|
||||
"keyboard_shortcuts.pinned": "Liste angehefteter Beiträge öffnen",
|
||||
"keyboard_shortcuts.profile": "Profil aufrufen",
|
||||
"keyboard_shortcuts.quote": "Beitrag zitieren",
|
||||
"keyboard_shortcuts.reply": "Auf Beitrag antworten",
|
||||
"keyboard_shortcuts.requests": "Liste der Follower-Anfragen aufrufen",
|
||||
"keyboard_shortcuts.search": "Suchleiste fokussieren",
|
||||
"keyboard_shortcuts.spoilers": "Feld für Inhaltswarnung anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.reply": "Beitrag beantworten",
|
||||
"keyboard_shortcuts.requests": "Follower-Anfragen aufrufen",
|
||||
"keyboard_shortcuts.search": "Eingabefeld / Suche fokussieren",
|
||||
"keyboard_shortcuts.spoilers": "Feld für Inhaltswarnung anzeigen / ausblenden",
|
||||
"keyboard_shortcuts.start": "„Auf gehts!“ öffnen",
|
||||
"keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.toggle_hidden": "Beitrag hinter Inhaltswarnung anzeigen / ausblenden",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen / ausblenden",
|
||||
"keyboard_shortcuts.toot": "Neuen Beitrag erstellen",
|
||||
"keyboard_shortcuts.top": "Zum Listenanfang springen",
|
||||
"keyboard_shortcuts.translate": "Beitrag übersetzen",
|
||||
"keyboard_shortcuts.unfocus": "Eingabefeld/Suche nicht mehr fokussieren",
|
||||
"keyboard_shortcuts.up": "Ansicht nach oben bewegen",
|
||||
"keyboard_shortcuts.unfocus": "Eingabefeld / Suche nicht mehr fokussieren",
|
||||
"keyboard_shortcuts.up": "Auswahl nach oben bewegen",
|
||||
"learn_more_link.got_it": "Verstanden",
|
||||
"learn_more_link.learn_more": "Mehr erfahren",
|
||||
"lightbox.close": "Schließen",
|
||||
|
|
@ -547,7 +547,7 @@
|
|||
"lists.done": "Fertig",
|
||||
"lists.edit": "Liste bearbeiten",
|
||||
"lists.exclusive": "Mitglieder auf der Startseite ausblenden",
|
||||
"lists.exclusive_hint": "Profile, die sich auf dieser Liste befinden, werden nicht auf deiner Startseite angezeigt, damit deren Beiträge nicht doppelt erscheinen.",
|
||||
"lists.exclusive_hint": "Profile, die sich auf dieser Liste befinden, werden nicht im Feed deiner Startseite angezeigt, damit deren Beiträge nicht doppelt erscheinen.",
|
||||
"lists.find_users_to_add": "Suche nach Profilen, um sie hinzuzufügen",
|
||||
"lists.list_members_count": "{count, plural, one {# Mitglied} other {# Mitglieder}}",
|
||||
"lists.list_name": "Titel der Liste",
|
||||
|
|
@ -556,19 +556,19 @@
|
|||
"lists.no_members_yet": "Keine Mitglieder vorhanden.",
|
||||
"lists.no_results_found": "Keine Suchergebnisse.",
|
||||
"lists.remove_member": "Entfernen",
|
||||
"lists.replies_policy.followed": "Alle folgenden Profile",
|
||||
"lists.replies_policy.followed": "alle folgenden Profile",
|
||||
"lists.replies_policy.list": "Mitglieder der Liste",
|
||||
"lists.replies_policy.none": "Niemanden",
|
||||
"lists.replies_policy.none": "niemanden",
|
||||
"lists.save": "Speichern",
|
||||
"lists.search": "Suchen",
|
||||
"lists.show_replies_to": "Antworten von Listenmitgliedern einbeziehen an …",
|
||||
"load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}",
|
||||
"loading_indicator.label": "Wird geladen …",
|
||||
"loading_indicator.label": "Lädt …",
|
||||
"media_gallery.hide": "Ausblenden",
|
||||
"moved_to_account_banner.text": "Dein Konto {disabledAccount} ist derzeit deaktiviert, weil du zu {movedToAccount} umgezogen bist.",
|
||||
"mute_modal.hide_from_notifications": "Benachrichtigungen ausblenden",
|
||||
"mute_modal.hide_options": "Einstellungen ausblenden",
|
||||
"mute_modal.indefinite": "Bis ich die Stummschaltung aufhebe",
|
||||
"mute_modal.hide_from_notifications": "Auch aus den Benachrichtigungen entfernen",
|
||||
"mute_modal.hide_options": "Optionen ausblenden",
|
||||
"mute_modal.indefinite": "Dauerhaft, bis ich die Stummschaltung aufhebe",
|
||||
"mute_modal.show_options": "Optionen anzeigen",
|
||||
"mute_modal.they_can_mention_and_follow": "Das Profil wird dich weiterhin erwähnen und dir folgen können, aber du wirst davon nichts sehen.",
|
||||
"mute_modal.they_wont_know": "Das Profil wird nicht erkennen können, dass du es stummgeschaltet hast.",
|
||||
|
|
@ -578,7 +578,7 @@
|
|||
"navigation_bar.about": "Über",
|
||||
"navigation_bar.account_settings": "Passwort und Sicherheit",
|
||||
"navigation_bar.administration": "Administration",
|
||||
"navigation_bar.advanced_interface": "Im erweiterten Webinterface öffnen",
|
||||
"navigation_bar.advanced_interface": "Erweitertes Webinterface öffnen",
|
||||
"navigation_bar.automated_deletion": "Automatisiertes Löschen",
|
||||
"navigation_bar.blocks": "Blockierte Profile",
|
||||
"navigation_bar.bookmarks": "Lesezeichen",
|
||||
|
|
@ -588,8 +588,8 @@
|
|||
"navigation_bar.filters": "Stummgeschaltete Wörter",
|
||||
"navigation_bar.follow_requests": "Follower-Anfragen",
|
||||
"navigation_bar.followed_tags": "Abonnierte Hashtags",
|
||||
"navigation_bar.follows_and_followers": "Follower und Folge ich",
|
||||
"navigation_bar.import_export": "Importieren und exportieren",
|
||||
"navigation_bar.follows_and_followers": "Follower & Folge ich",
|
||||
"navigation_bar.import_export": "Importieren & exportieren",
|
||||
"navigation_bar.lists": "Listen",
|
||||
"navigation_bar.live_feed_local": "Live-Feed (Dieser Server)",
|
||||
"navigation_bar.live_feed_public": "Live-Feed (Alle Server)",
|
||||
|
|
@ -599,9 +599,9 @@
|
|||
"navigation_bar.mutes": "Stummgeschaltete Profile",
|
||||
"navigation_bar.opened_in_classic_interface": "Beiträge, Konten und andere bestimmte Seiten werden standardmäßig im klassischen Webinterface geöffnet.",
|
||||
"navigation_bar.preferences": "Einstellungen",
|
||||
"navigation_bar.privacy_and_reach": "Datenschutz und Reichweite",
|
||||
"navigation_bar.privacy_and_reach": "Datenschutz & Reichweite",
|
||||
"navigation_bar.search": "Suche",
|
||||
"navigation_bar.search_trends": "Suche / Angesagt",
|
||||
"navigation_bar.search_trends": "Suche / Trends",
|
||||
"navigation_panel.collapse_followed_tags": "Menü für abonnierte Hashtags schließen",
|
||||
"navigation_panel.collapse_lists": "Listen-Menü schließen",
|
||||
"navigation_panel.expand_followed_tags": "Menü für abonnierte Hashtags öffnen",
|
||||
|
|
@ -635,7 +635,7 @@
|
|||
"notification.moderation_warning": "Du wurdest von den Moderator*innen verwarnt",
|
||||
"notification.moderation_warning.action_delete_statuses": "Einige deiner Beiträge sind entfernt worden.",
|
||||
"notification.moderation_warning.action_disable": "Dein Konto wurde deaktiviert.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Einige deiner Beiträge wurden mit einer Inhaltswarnung versehen.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Einige deiner Beiträge haben eine Inhaltswarnung erhalten.",
|
||||
"notification.moderation_warning.action_none": "Dein Konto ist von den Moderator*innen verwarnt worden.",
|
||||
"notification.moderation_warning.action_sensitive": "Deine zukünftigen Beiträge werden mit einer Inhaltswarnung versehen.",
|
||||
"notification.moderation_warning.action_silence": "Dein Konto wurde eingeschränkt.",
|
||||
|
|
@ -650,7 +650,7 @@
|
|||
"notification.relationships_severance_event.domain_block": "Ein Admin von {from} hat {target} blockiert – darunter {followersCount} deiner Follower und {followingCount, plural, one {# Konto, dem} other {# Konten, denen}} du folgst.",
|
||||
"notification.relationships_severance_event.learn_more": "Mehr erfahren",
|
||||
"notification.relationships_severance_event.user_domain_block": "Du hast {target} blockiert – {followersCount} deiner Follower und {followingCount, plural, one {# Konto, dem} other {# Konten, denen}} du folgst, wurden entfernt.",
|
||||
"notification.status": "{name} postete …",
|
||||
"notification.status": "{name} veröffentlichte …",
|
||||
"notification.update": "{name} bearbeitete einen Beitrag",
|
||||
"notification_requests.accept": "Akzeptieren",
|
||||
"notification_requests.accept_multiple": "{count, plural, one {# Anfrage akzeptieren …} other {# Anfragen akzeptieren …}}",
|
||||
|
|
@ -686,9 +686,9 @@
|
|||
"notifications.column_settings.mention": "Erwähnungen:",
|
||||
"notifications.column_settings.poll": "Umfrageergebnisse:",
|
||||
"notifications.column_settings.push": "Push-Benachrichtigungen",
|
||||
"notifications.column_settings.quote": "Zitate:",
|
||||
"notifications.column_settings.quote": "Zitierte Beiträge:",
|
||||
"notifications.column_settings.reblog": "Geteilte Beiträge:",
|
||||
"notifications.column_settings.show": "In dieser Spalte anzeigen",
|
||||
"notifications.column_settings.show": "Im Feed „Benachrichtigungen“ anzeigen",
|
||||
"notifications.column_settings.sound": "Ton abspielen",
|
||||
"notifications.column_settings.status": "Neue Beiträge:",
|
||||
"notifications.column_settings.unread_notifications.category": "Ungelesene Benachrichtigungen",
|
||||
|
|
@ -697,10 +697,10 @@
|
|||
"notifications.filter.all": "Alles",
|
||||
"notifications.filter.boosts": "Geteilte Beiträge",
|
||||
"notifications.filter.favourites": "Favoriten",
|
||||
"notifications.filter.follows": "Folgt",
|
||||
"notifications.filter.follows": "Neue Follower",
|
||||
"notifications.filter.mentions": "Erwähnungen",
|
||||
"notifications.filter.polls": "Umfrageergebnisse",
|
||||
"notifications.filter.statuses": "Neue Beiträge von Profilen, denen du folgst",
|
||||
"notifications.filter.statuses": "Neue Beiträge von abonnierten Profilen",
|
||||
"notifications.grant_permission": "Berechtigung erteilen.",
|
||||
"notifications.group": "{count} Benachrichtigungen",
|
||||
"notifications.mark_as_read": "Alle Benachrichtigungen als gelesen markieren",
|
||||
|
|
@ -710,18 +710,18 @@
|
|||
"notifications.policy.accept": "Akzeptieren",
|
||||
"notifications.policy.accept_hint": "In Benachrichtigungen anzeigen",
|
||||
"notifications.policy.drop": "Ignorieren",
|
||||
"notifications.policy.drop_hint": "In die Leere senden und nie wieder sehen",
|
||||
"notifications.policy.drop_hint": "Ins Nirwana befördern und auf Nimmerwiedersehen!",
|
||||
"notifications.policy.filter": "Filtern",
|
||||
"notifications.policy.filter_hint": "An gefilterte Benachrichtigungen im Posteingang senden",
|
||||
"notifications.policy.filter_limited_accounts_hint": "Durch Server-Moderator*innen eingeschränkt",
|
||||
"notifications.policy.filter_limited_accounts_title": "moderierten Konten",
|
||||
"notifications.policy.filter_new_accounts.hint": "Innerhalb {days, plural, one {des letzten Tages} other {der letzten # Tagen}} erstellt",
|
||||
"notifications.policy.filter_new_accounts_title": "neuen Konten",
|
||||
"notifications.policy.filter_not_followers_hint": "Einschließlich Profilen, die dir seit weniger als {days, plural, one {einem Tag} other {# Tagen}} folgen",
|
||||
"notifications.policy.filter_hint": "Im separaten Feed „Gefilterte Benachrichtigungen“ anzeigen",
|
||||
"notifications.policy.filter_limited_accounts_hint": "Durch Server-Moderator*innen eingeschränkte Profile",
|
||||
"notifications.policy.filter_limited_accounts_title": "eingeschränkten Konten",
|
||||
"notifications.policy.filter_new_accounts.hint": "Konto {days, plural, one {seit gestern} other {in den vergangenen # Tagen}} registriert",
|
||||
"notifications.policy.filter_new_accounts_title": "neuen Profilen",
|
||||
"notifications.policy.filter_not_followers_hint": "Einschließlich Profilen, die mir seit weniger als {days, plural, one {einem Tag} other {# Tagen}} folgen",
|
||||
"notifications.policy.filter_not_followers_title": "Profilen, die mir nicht folgen",
|
||||
"notifications.policy.filter_not_following_hint": "Bis du sie manuell genehmigst",
|
||||
"notifications.policy.filter_not_following_hint": "… bis ich sie manuell genehmige",
|
||||
"notifications.policy.filter_not_following_title": "Profilen, denen ich nicht folge",
|
||||
"notifications.policy.filter_private_mentions_hint": "Solange sie keine Antwort auf deine Erwähnung ist oder du dem Profil nicht folgst",
|
||||
"notifications.policy.filter_private_mentions_hint": "… solange sie keine Antwort auf meine Erwähnungen sind – oder ich den Profilen nicht folge",
|
||||
"notifications.policy.filter_private_mentions_title": "unerwünschten privaten Erwähnungen",
|
||||
"notifications.policy.title": "Benachrichtigungen verwalten von …",
|
||||
"notifications_permission_banner.enable": "Aktiviere Desktop-Benachrichtigungen",
|
||||
|
|
@ -763,17 +763,17 @@
|
|||
"privacy.public.long": "Alle innerhalb und außerhalb von Mastodon",
|
||||
"privacy.public.short": "Öffentlich",
|
||||
"privacy.quote.anyone": "{visibility} – alle dürfen zitieren",
|
||||
"privacy.quote.disabled": "{visibility} – niemand darf zitieren",
|
||||
"privacy.quote.limited": "{visibility} – eingeschränktes Zitieren",
|
||||
"privacy.quote.disabled": "{visibility} – Zitieren deaktiviert",
|
||||
"privacy.quote.limited": "{visibility} – nur Follower",
|
||||
"privacy.unlisted.additional": "Das Verhalten ist wie bei „Öffentlich“, jedoch gibt es einige Einschränkungen. Der Beitrag wird nicht in „Live-Feeds“, „Erkunden“, Hashtags oder über die Mastodon-Suchfunktion auffindbar sein – selbst wenn die zugehörige Einstellung aktiviert wurde.",
|
||||
"privacy.unlisted.long": "Verborgen vor Suchergebnissen, Trends und öffentlichen Timelines",
|
||||
"privacy.unlisted.long": "Verborgen vor Suchen, Trends und öffentlichen Timelines",
|
||||
"privacy.unlisted.short": "Öffentlich (still)",
|
||||
"privacy_policy.last_updated": "Stand: {date}",
|
||||
"privacy_policy.title": "Datenschutzerklärung",
|
||||
"quote_error.edit": "Beim Bearbeiten eines Beitrags können keine Zitate hinzugefügt werden.",
|
||||
"quote_error.poll": "Zitieren ist bei Umfragen nicht gestattet.",
|
||||
"quote_error.private_mentions": "Das Zitieren ist bei privaten Erwähnungen nicht erlaubt.",
|
||||
"quote_error.quote": "Es ist jeweils nur ein Zitat zulässig.",
|
||||
"quote_error.edit": "Beim Bearbeiten eines vorhandenen Beitrags können keine Zitate hinzugefügt werden.",
|
||||
"quote_error.poll": "Zitieren ist bei Umfragen nicht erlaubt.",
|
||||
"quote_error.private_mentions": "Zitieren ist bei privaten Erwähnungen nicht erlaubt.",
|
||||
"quote_error.quote": "Es darf nur ein Beitrag zitiert werden.",
|
||||
"quote_error.unauthorized": "Du bist nicht berechtigt, diesen Beitrag zu zitieren.",
|
||||
"quote_error.upload": "Zitieren ist mit Medien-Anhängen nicht möglich.",
|
||||
"recommended": "Empfohlen",
|
||||
|
|
@ -783,7 +783,7 @@
|
|||
"relative_time.days": "{number} T.",
|
||||
"relative_time.full.days": "vor {number, plural, one {# Tag} other {# Tagen}}",
|
||||
"relative_time.full.hours": "vor {number, plural, one {# Stunde} other {# Stunden}}",
|
||||
"relative_time.full.just_now": "gerade eben",
|
||||
"relative_time.full.just_now": "soeben",
|
||||
"relative_time.full.minutes": "vor {number, plural, one {# Minute} other {# Minuten}}",
|
||||
"relative_time.full.seconds": "vor {number, plural, one {1 Sekunde} other {# Sekunden}}",
|
||||
"relative_time.hours": "{number} Std.",
|
||||
|
|
@ -793,13 +793,13 @@
|
|||
"relative_time.today": "heute",
|
||||
"remove_quote_hint.button_label": "Verstanden",
|
||||
"remove_quote_hint.message": "Klicke dafür im Beitrag auf „{icon} Mehr“.",
|
||||
"remove_quote_hint.title": "Möchtest du aus dem zitierten Beitrag entfernt werden?",
|
||||
"remove_quote_hint.title": "Deinen zitierten Beitrag aus diesem Beitrag entfernen?",
|
||||
"reply_indicator.attachments": "{count, plural, one {# Anhang} other {# Anhänge}}",
|
||||
"reply_indicator.cancel": "Abbrechen",
|
||||
"reply_indicator.poll": "Umfrage",
|
||||
"report.block": "Blockieren",
|
||||
"report.block_explanation": "Du wirst keine Beiträge mehr von diesem Konto sehen. Das blockierte Konto wird deine Beiträge nicht mehr sehen oder dir folgen können. Die Person könnte mitbekommen, dass du sie blockiert hast.",
|
||||
"report.categories.legal": "Rechtlich",
|
||||
"report.categories.legal": "Rechtliches",
|
||||
"report.categories.other": "Andere",
|
||||
"report.categories.spam": "Spam",
|
||||
"report.categories.violation": "Der Inhalt verletzt eine oder mehrere Serverregeln",
|
||||
|
|
@ -808,9 +808,9 @@
|
|||
"report.category.title_account": "Profil",
|
||||
"report.category.title_status": "Beitrag",
|
||||
"report.close": "Fertig",
|
||||
"report.comment.title": "Gibt es etwas anderes, was wir wissen sollten?",
|
||||
"report.forward": "Meldung zusätzlich an {target} weiterleiten",
|
||||
"report.forward_hint": "Dieses Konto gehört zu einem anderen Server. Soll eine anonymisierte Kopie der Meldung auch dorthin gesendet werden?",
|
||||
"report.comment.title": "Gibt es noch etwas, das wir wissen sollten?",
|
||||
"report.forward": "Meldung auch an den externen Server {target} weiterleiten",
|
||||
"report.forward_hint": "Das gemeldete Konto befindet sich auf einem anderen Server. Soll zusätzlich eine anonymisierte Kopie deiner Meldung an diesen Server geschickt werden?",
|
||||
"report.mute": "Stummschalten",
|
||||
"report.mute_explanation": "Du wirst keine Beiträge mehr von diesem Konto sehen. Das stummgeschaltete Konto wird dir weiterhin folgen und deine Beiträge sehen können. Die Person wird nicht mitbekommen, dass du sie stummgeschaltet hast.",
|
||||
"report.next": "Weiter",
|
||||
|
|
@ -826,9 +826,9 @@
|
|||
"report.reasons.violation": "Das verstößt gegen Serverregeln",
|
||||
"report.reasons.violation_description": "Du bist dir sicher, dass eine bestimmte Regel gebrochen wurde",
|
||||
"report.rules.subtitle": "Wähle alle zutreffenden Inhalte aus",
|
||||
"report.rules.title": "Welche Regeln werden verletzt?",
|
||||
"report.rules.title": "Gegen welche Regeln wurde verstoßen?",
|
||||
"report.statuses.subtitle": "Wähle alle zutreffenden Inhalte aus",
|
||||
"report.statuses.title": "Gibt es Beiträge, die diese Meldung bekräftigen?",
|
||||
"report.statuses.title": "Gibt es Beiträge, die diese Meldung stützen?",
|
||||
"report.submit": "Senden",
|
||||
"report.target": "{target} melden",
|
||||
"report.thanks.take_action": "Das sind deine Möglichkeiten zu bestimmen, was du auf Mastodon sehen möchtest:",
|
||||
|
|
@ -845,7 +845,7 @@
|
|||
"report_notification.categories.spam": "Spam",
|
||||
"report_notification.categories.spam_sentence": "Spam",
|
||||
"report_notification.categories.violation": "Regelverstoß",
|
||||
"report_notification.categories.violation_sentence": "Regelverletzung",
|
||||
"report_notification.categories.violation_sentence": "Verstoß gegen die Serverregeln",
|
||||
"report_notification.open": "Meldung öffnen",
|
||||
"search.clear": "Suchanfrage löschen",
|
||||
"search.no_recent_searches": "Keine früheren Suchanfragen",
|
||||
|
|
@ -885,13 +885,13 @@
|
|||
"status.admin_account": "@{name} moderieren",
|
||||
"status.admin_domain": "{domain} moderieren",
|
||||
"status.admin_status": "Beitrag moderieren",
|
||||
"status.all_disabled": "Teilen und Zitieren von Beiträgen ist deaktiviert",
|
||||
"status.all_disabled": "Teilen und Zitieren sind deaktiviert",
|
||||
"status.block": "@{name} blockieren",
|
||||
"status.bookmark": "Lesezeichen setzen",
|
||||
"status.cancel_reblog_private": "Beitrag nicht mehr teilen",
|
||||
"status.cannot_quote": "Beitrag kann nicht zitiert werden",
|
||||
"status.cannot_quote": "Diesen Beitrag darfst du nicht zitieren",
|
||||
"status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
|
||||
"status.contains_quote": "Enthält Zitat",
|
||||
"status.contains_quote": "Enthält zitierten Beitrag",
|
||||
"status.context.loading": "Weitere Antworten laden",
|
||||
"status.context.loading_error": "Weitere Antworten konnten nicht geladen werden",
|
||||
"status.context.loading_success": "Neue Antworten geladen",
|
||||
|
|
@ -907,10 +907,10 @@
|
|||
"status.direct_indicator": "Private Erwähnung",
|
||||
"status.edit": "Beitrag bearbeiten",
|
||||
"status.edited": "Zuletzt am {date} bearbeitet",
|
||||
"status.edited_x_times": "{count, plural, one {{count}-mal} other {{count}-mal}} bearbeitet",
|
||||
"status.edited_x_times": "{count, plural, one {{count} ×} other {{count} ×}} bearbeitet",
|
||||
"status.embed": "Code zum Einbetten",
|
||||
"status.favourite": "Favorisieren",
|
||||
"status.favourites_count": "{count, plural, one {{counter} Mal favorisiert} other {{counter} Mal favorisiert}}",
|
||||
"status.favourites_count": "{count, plural, one {{counter} × favorisiert} other {{counter} × favorisiert}}",
|
||||
"status.filter": "Beitrag filtern",
|
||||
"status.history.created": "{name} erstellte {date}",
|
||||
"status.history.edited": "{name} bearbeitete {date}",
|
||||
|
|
@ -945,14 +945,14 @@
|
|||
"status.quotes.empty": "Diesen Beitrag hat bisher noch niemand zitiert. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
||||
"status.quotes.local_other_disclaimer": "Durch Autor*in abgelehnte Zitate werden nicht angezeigt.",
|
||||
"status.quotes.remote_other_disclaimer": "Nur Zitate von {domain} werden hier garantiert angezeigt. Durch Autor*in abgelehnte Zitate werden nicht angezeigt.",
|
||||
"status.quotes_count": "{count, plural, one {{counter} Mal zitiert} other {{counter} Mal zitiert}}",
|
||||
"status.quotes_count": "{count, plural, one {{counter} × zitiert} other {{counter} × zitiert}}",
|
||||
"status.read_more": "Gesamten Beitrag anschauen",
|
||||
"status.reblog": "Teilen",
|
||||
"status.reblog_or_quote": "Teilen oder zitieren",
|
||||
"status.reblog_private": "Erneut mit deinen Followern teilen",
|
||||
"status.reblogged_by": "{name} teilte",
|
||||
"status.reblogs.empty": "Diesen Beitrag hat bisher noch niemand geteilt. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
||||
"status.reblogs_count": "{count, plural, one {{counter} Mal geteilt} other {{counter} Mal geteilt}}",
|
||||
"status.reblogs_count": "{count, plural, one {{counter} × geteilt} other {{counter} × geteilt}}",
|
||||
"status.redraft": "Löschen und neu erstellen",
|
||||
"status.remove_bookmark": "Lesezeichen entfernen",
|
||||
"status.remove_favourite": "Aus Favoriten entfernen",
|
||||
|
|
@ -1036,9 +1036,9 @@
|
|||
"visibility_modal.helper.unlisted_quoting": "Sollten dich andere zitieren, werden ihre zitierten Beiträge ebenfalls nicht in den Trends und öffentlichen Timelines angezeigt.",
|
||||
"visibility_modal.instructions": "Lege fest, wer mit diesem Beitrag interagieren darf. Du hast auch die Möglichkeit, diese Einstellung auf alle zukünftigen Beiträge anzuwenden. Gehe zu: <link>Einstellungen > Standardeinstellungen für Beiträge</link>",
|
||||
"visibility_modal.privacy_label": "Sichtbarkeit",
|
||||
"visibility_modal.quote_followers": "Nur Follower",
|
||||
"visibility_modal.quote_followers": "Nur meine Follower dürfen mich zitieren",
|
||||
"visibility_modal.quote_label": "Wer darf mich zitieren?",
|
||||
"visibility_modal.quote_nobody": "Nur ich selbst",
|
||||
"visibility_modal.quote_public": "Alle",
|
||||
"visibility_modal.quote_nobody": "Niemand darf mich zitieren",
|
||||
"visibility_modal.quote_public": "Alle dürfen mich zitieren",
|
||||
"visibility_modal.save": "Speichern"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,26 +117,41 @@
|
|||
"annual_report.announcement.action_view": "View my Wrapstodon",
|
||||
"annual_report.announcement.description": "Discover more about your engagement on Mastodon over the past year.",
|
||||
"annual_report.announcement.title": "Wrapstodon {year} has arrived",
|
||||
"annual_report.summary.archetype.booster": "The cool-hunter",
|
||||
"annual_report.summary.archetype.lurker": "The lurker",
|
||||
"annual_report.summary.archetype.oracle": "The oracle",
|
||||
"annual_report.summary.archetype.pollster": "The pollster",
|
||||
"annual_report.summary.archetype.replier": "The social butterfly",
|
||||
"annual_report.summary.followers.followers": "followers",
|
||||
"annual_report.summary.followers.total": "{count} total",
|
||||
"annual_report.summary.here_it_is": "Here is your {year} in review:",
|
||||
"annual_report.summary.highlighted_post.by_favourites": "most favourited post",
|
||||
"annual_report.summary.highlighted_post.by_reblogs": "most boosted post",
|
||||
"annual_report.summary.highlighted_post.by_replies": "post with the most replies",
|
||||
"annual_report.summary.highlighted_post.possessive": "{name}'s",
|
||||
"annual_report.shared_page.footer": "Generated with {heart} by the Mastodon team",
|
||||
"annual_report.summary.archetype.booster.desc_public": "{name} stayed on the hunt for posts to boost, amplifying other creators with perfect aim.",
|
||||
"annual_report.summary.archetype.booster.desc_self": "You stayed on the hunt for posts to boost, amplifying other creators with perfect aim.",
|
||||
"annual_report.summary.archetype.booster.name": "The Archer",
|
||||
"annual_report.summary.archetype.die_drei_fragezeichen": "???",
|
||||
"annual_report.summary.archetype.lurker.desc_public": "We know {name} was out there, somewhere, enjoying Mastodon in their own quiet way.",
|
||||
"annual_report.summary.archetype.lurker.desc_self": "We know you were out there, somewhere, enjoying Mastodon in your own quiet way.",
|
||||
"annual_report.summary.archetype.lurker.name": "The Stoic",
|
||||
"annual_report.summary.archetype.oracle.desc_public": "{name} created new posts more than replies, keeping Mastodon fresh and future-facing.",
|
||||
"annual_report.summary.archetype.oracle.desc_self": "You created new posts more than replies, keeping Mastodon fresh and future-facing.",
|
||||
"annual_report.summary.archetype.oracle.name": "The Oracle",
|
||||
"annual_report.summary.archetype.pollster.desc_public": "{name} created more polls than other post types, cultivating curiosity on Mastodon.",
|
||||
"annual_report.summary.archetype.pollster.desc_self": "You created more polls than other post types, cultivating curiosity on Mastodon.",
|
||||
"annual_report.summary.archetype.pollster.name": "The Wonderer",
|
||||
"annual_report.summary.archetype.replier.desc_public": "{name} frequently replied to other people’s posts, pollinating Mastodon with new discussions.",
|
||||
"annual_report.summary.archetype.replier.desc_self": "You frequently replied to other people’s posts, pollinating Mastodon with new discussions.",
|
||||
"annual_report.summary.archetype.replier.name": "The Butterfly",
|
||||
"annual_report.summary.archetype.reveal": "Reveal my archetype",
|
||||
"annual_report.summary.archetype.reveal_description": "Thanks for being part of Mastodon! Time to find out which archetype you embodied in {year}.",
|
||||
"annual_report.summary.archetype.title_public": "{name}'s archetype",
|
||||
"annual_report.summary.archetype.title_self": "Your archetype",
|
||||
"annual_report.summary.close": "Close",
|
||||
"annual_report.summary.followers.new_followers": "{count, plural, one {new follower} other {new followers}}",
|
||||
"annual_report.summary.highlighted_post.boost_count": "This post was boosted {count, plural, one {once} other {# times}}.",
|
||||
"annual_report.summary.highlighted_post.favourite_count": "This post was favorited {count, plural, one {once} other {# times}}.",
|
||||
"annual_report.summary.highlighted_post.reply_count": "This post got {count, plural, one {one reply} other {# replies}}.",
|
||||
"annual_report.summary.highlighted_post.title": "Most popular post",
|
||||
"annual_report.summary.most_used_app.most_used_app": "most used app",
|
||||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "most used hashtag",
|
||||
"annual_report.summary.most_used_hashtag.none": "None",
|
||||
"annual_report.summary.most_used_hashtag.used_count": "You included this hashtag in {count, plural, one {one post} other {# posts}}.",
|
||||
"annual_report.summary.most_used_hashtag.used_count_public": "{name} included this hashtag in {count, plural, one {one post} other {# posts}}.",
|
||||
"annual_report.summary.new_posts.new_posts": "new posts",
|
||||
"annual_report.summary.percentile.text": "<topLabel>That puts you in the top</topLabel><percentage></percentage><bottomLabel>of {domain} users.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "We won't tell Bernie.",
|
||||
"annual_report.summary.share_message": "I got the {archetype} archetype!",
|
||||
"annual_report.summary.thanks": "Thanks for being part of Mastodon!",
|
||||
"attachments_list.unprocessed": "(unprocessed)",
|
||||
"audio.hide": "Hide audio",
|
||||
"block_modal.remote_users_caveat": "We will ask the server {domain} to respect your decision. However, compliance is not guaranteed since some servers may handle blocks differently. Public posts may still be visible to non-logged-in users.",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"about.contact": "Kontakt:",
|
||||
"about.default_locale": "Vaikimisi",
|
||||
"about.disclaimer": "Mastodon on tasuta ja vaba tarkvara ning Mastodon gGmbH kaubamärk.",
|
||||
"about.domain_blocks.no_reason_available": "Põhjus teadmata",
|
||||
"about.domain_blocks.no_reason_available": "Põhjus on teadmata",
|
||||
"about.domain_blocks.preamble": "Mastodon lubab tavaliselt vaadata sisu ning suhelda kasutajatega ükskõik millisest teisest fediversumi serverist. Need on erandid, mis on paika pandud sellel kindlal serveril.",
|
||||
"about.domain_blocks.silenced.explanation": "Sa ei näe üldiselt profiile ja sisu sellelt serverilt, kui sa just tahtlikult seda ei otsi või jälgimise moel nõusolekut ei anna.",
|
||||
"about.domain_blocks.silenced.title": "Piiratud",
|
||||
|
|
@ -113,6 +113,10 @@
|
|||
"alt_text_modal.describe_for_people_with_visual_impairments": "Kirjelda seda nägemispuudega inimeste jaoks…",
|
||||
"alt_text_modal.done": "Valmis",
|
||||
"announcement.announcement": "Teadaanne",
|
||||
"annual_report.announcement.action_build": "Koosta kokkuvõte minu tegevusest Mastodonis",
|
||||
"annual_report.announcement.action_view": "Vaata kokkuvõtet minu tegevusest Mastodonis",
|
||||
"annual_report.announcement.description": "Vaata teavet oma suhestumise kohta Mastodonis eelmisel aastal.",
|
||||
"annual_report.announcement.title": "{year}. aasta Mastodoni kokkuvõte on valmis",
|
||||
"annual_report.summary.archetype.booster": "Ägesisu küttija",
|
||||
"annual_report.summary.archetype.lurker": "Hiilija",
|
||||
"annual_report.summary.archetype.oracle": "Oraakel",
|
||||
|
|
@ -131,6 +135,7 @@
|
|||
"annual_report.summary.new_posts.new_posts": "uus postitus",
|
||||
"annual_report.summary.percentile.text": "<topLabel>See paneb su top</topLabel><percentage></percentage><bottomLabel> {domain} kasutajate hulka.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Vägev.",
|
||||
"annual_report.summary.share_message": "Minu arhetüüp on {archetype}!",
|
||||
"annual_report.summary.thanks": "Tänud olemast osa Mastodonist!",
|
||||
"attachments_list.unprocessed": "(töötlemata)",
|
||||
"audio.hide": "Peida audio",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"about.default_locale": "پیشگزیده",
|
||||
"about.disclaimer": "ماستودون نرمافزار آزاد و نشان تجاری یک شرکت غیر انتفاعی با مسئولیت محدود آلمانی است.",
|
||||
"about.domain_blocks.no_reason_available": "دلیلی موجود نیست",
|
||||
"about.domain_blocks.preamble": "ماستودون عموماً میگذارد محتوا را از از هر کارساز دیگری در دنیای شبکههای اجتماعی غیرمتمرکز دیده و با آنان برهمکنش داشته باشید. اینها استثناهایی هستند که روی این کارساز خاص وضع شدهاند.",
|
||||
"about.domain_blocks.preamble": "ماستودون عموماً میگذارد محتوا را از هر کارساز دیگری در دنیای شبکههای اجتماعی غیرمتمرکز دیده و با آنان برهمکنش داشته باشید. اینها استثناهایی هستند که روی این کارساز خاص وضع شدهاند.",
|
||||
"about.domain_blocks.silenced.explanation": "عموماً نمایهها و محتوا از این کارساز را نمیبینید، مگر این که به طور خاص دنبالشان گشته یا با پی گیری، داوطلب دیدنشان شوید.",
|
||||
"about.domain_blocks.silenced.title": "محدود",
|
||||
"about.domain_blocks.suspended.explanation": "هیچ دادهای از این کارساز پردازش، ذخیره یا مبادله نخواهد شد، که هرگونه برهمکنش یا ارتباط با کاربران این کارساز را غیرممکن خواهد کرد.",
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
"annual_report.summary.archetype.lurker": "کمپیدا",
|
||||
"annual_report.summary.archetype.oracle": "غیبگو",
|
||||
"annual_report.summary.archetype.pollster": "نظرسنج",
|
||||
"annual_report.summary.archetype.replier": "پاسخگو",
|
||||
"annual_report.summary.archetype.replier": "پاسخگو",
|
||||
"annual_report.summary.followers.followers": "دنبال کننده",
|
||||
"annual_report.summary.followers.total": "در مجموع {count}",
|
||||
"annual_report.summary.here_it_is": "بازبینی {year} تان:",
|
||||
|
|
@ -135,6 +135,7 @@
|
|||
"annual_report.summary.new_posts.new_posts": "فرستهٔ جدید",
|
||||
"annual_report.summary.percentile.text": "<topLabel>بین کاربران {domain} جزو</topLabel><percentage></percentage><bottomLabel>برتر هستید.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "به برنی خبر نمیدهیم.",
|
||||
"annual_report.summary.share_message": "من کهنالگوی {archetype} را گرفتم!",
|
||||
"annual_report.summary.thanks": "سپاس که بخشی از ماستودون هستید!",
|
||||
"attachments_list.unprocessed": "(پردازش نشده)",
|
||||
"audio.hide": "نهفتن صدا",
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@
|
|||
"error.no_hashtag_feed_access": "Liity tai kirjaudu sisään, niin voit tarkastella ja seurata tätä aihetunnistetta.",
|
||||
"error.unexpected_crash.explanation": "Sivua ei voida näyttää oikein ohjelmointivirheen tai selaimen yhteensopivuusvajeen vuoksi.",
|
||||
"error.unexpected_crash.explanation_addons": "Sivua ei voitu näyttää oikein. Tämä virhe johtuu todennäköisesti selaimen lisäosasta tai automaattisista käännöstyökaluista.",
|
||||
"error.unexpected_crash.next_steps": "Kokeile päivittää sivu. Jos se ei auta, voi Mastodonin käyttö ehkä onnistua eri selaimella tai natiivisovelluksella.",
|
||||
"error.unexpected_crash.next_steps": "Kokeile päivittää sivu. Jos se ei auta, Mastodonin käyttö voi ehkä onnistua eri selaimella tai natiivisovelluksella.",
|
||||
"error.unexpected_crash.next_steps_addons": "Yritä poistaa ne käytöstä, ja virkistä sitten sivunlataus. Mikäli ongelma jatkuu, voit mahdollisesti käyttää Mastodonia eri selaimella tai natiivilla sovelluksella.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopioi pinon jäljitys leikepöydälle",
|
||||
"errors.unexpected_crash.report_issue": "Ilmoita ongelmasta",
|
||||
|
|
@ -867,7 +867,7 @@
|
|||
"search_results.all": "Kaikki",
|
||||
"search_results.hashtags": "Aihetunnisteet",
|
||||
"search_results.no_results": "Ei tuloksia.",
|
||||
"search_results.no_search_yet": "Koeta hakea julkaisuja, profiileja tai aihetunnisteita.",
|
||||
"search_results.no_search_yet": "Kokeile hakea julkaisuja, profiileja tai aihetunnisteita.",
|
||||
"search_results.see_all": "Näytä kaikki",
|
||||
"search_results.statuses": "Julkaisut",
|
||||
"search_results.title": "Haku ”{q}”",
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@
|
|||
"annual_report.summary.new_posts.new_posts": "nýggir postar",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Tað fær teg í topp</topLabel><percentage></percentage><bottomLabel>av {domain} brúkarum.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Vit fara ikki at fortelja Bernie tað.",
|
||||
"annual_report.summary.share_message": "Eg fekk {archetype} frumsniðið!",
|
||||
"annual_report.summary.thanks": "Takk fyri at tú er partur av Mastodon!",
|
||||
"attachments_list.unprocessed": "(óviðgjørt)",
|
||||
"audio.hide": "Fjal ljóð",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
"account.familiar_followers_two": "{name1} ir {name2} seka",
|
||||
"account.featured": "Rodomi",
|
||||
"account.featured.accounts": "Profiliai",
|
||||
"account.featured.hashtags": "Saitažodžiai",
|
||||
"account.featured.hashtags": "Grotažymės",
|
||||
"account.featured_tags.last_status_at": "Paskutinis įrašas {date}",
|
||||
"account.featured_tags.last_status_never": "Nėra įrašų",
|
||||
"account.follow": "Sekti",
|
||||
|
|
@ -130,7 +130,7 @@
|
|||
"annual_report.summary.highlighted_post.by_replies": "įrašas su daugiausiai atsakymų",
|
||||
"annual_report.summary.highlighted_post.possessive": "{name}",
|
||||
"annual_report.summary.most_used_app.most_used_app": "labiausiai naudota programa",
|
||||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "labiausiai naudotas saitažodis",
|
||||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "labiausiai naudota grotažymė",
|
||||
"annual_report.summary.most_used_hashtag.none": "Nieko",
|
||||
"annual_report.summary.new_posts.new_posts": "nauji įrašai",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Tai reiškia, kad esate tarp</topLabel><percentage></percentage><bottomLabel>populiariausių {domain} naudotojų.</bottomLabel>",
|
||||
|
|
@ -209,7 +209,7 @@
|
|||
"compose.saved.body": "Įrašas išsaugotas.",
|
||||
"compose_form.direct_message_warning_learn_more": "Sužinoti daugiau",
|
||||
"compose_form.encryption_warning": "„Mastodon“ įrašai nėra visapusiškai šifruojami. Per „Mastodon“ nesidalyk jokia slapta informacija.",
|
||||
"compose_form.hashtag_warning": "Šis įrašas nebus įtrauktas į jokį saitažodį, nes ji nėra vieša. Tik viešų įrašų galima ieškoti pagal saitažodį.",
|
||||
"compose_form.hashtag_warning": "Šis įrašas nebus įtrauktas į jokį grotažodį, nes jis nėra viešas. Tik vieši įrašai gali būti ieškomi pagal grotažymę.",
|
||||
"compose_form.lock_disclaimer": "Tavo paskyra nėra {locked}. Bet kas gali sekti tave ir peržiūrėti tik sekėjams skirtus įrašus.",
|
||||
"compose_form.lock_disclaimer.lock": "užrakinta",
|
||||
"compose_form.placeholder": "Kas tavo mintyse?",
|
||||
|
|
@ -337,15 +337,15 @@
|
|||
"emoji_button.search_results": "Paieškos rezultatai",
|
||||
"emoji_button.symbols": "Simboliai",
|
||||
"emoji_button.travel": "Kelionės ir vietos",
|
||||
"empty_column.account_featured.me": "Jūs dar nieko neparyškinote. Ar žinojote, kad savo profilyje galite parodyti dažniausiai naudojamas žymes ir netgi savo draugų paskyras?",
|
||||
"empty_column.account_featured.other": "{acct} dar nieko neparyškino. Ar žinojote, kad savo profilyje galite pateikti dažniausiai naudojamus žymes ir netgi savo draugų paskyras?",
|
||||
"empty_column.account_featured.me": "Jūs dar nieko neparyškinote. Ar žinojote, kad savo profilyje galite parodyti dažniausiai naudojamas grotažymes ir netgi savo draugų paskyras?",
|
||||
"empty_column.account_featured.other": "{acct} dar nieko neparyškino. Ar žinojote, kad savo profilyje galite pateikti dažniausiai naudojamus grotžymes ir netgi savo draugų paskyras?",
|
||||
"empty_column.account_featured_other.unknown": "Ši paskyra dar nieko neparodė.",
|
||||
"empty_column.account_hides_collections": "Šis (-i) naudotojas (-a) pasirinko nepadaryti šią informaciją prieinamą.",
|
||||
"empty_column.account_suspended": "Paskyra pristabdyta.",
|
||||
"empty_column.account_timeline": "Nėra čia įrašų.",
|
||||
"empty_column.account_unavailable": "Profilis neprieinamas.",
|
||||
"empty_column.blocks": "Dar neužblokavai nė vieno naudotojo.",
|
||||
"empty_column.bookmarked_statuses": "Dar neturi nė vienos įrašo pridėtos žymės. Kai vieną iš jų pridėsi į žymes, jis bus rodomas čia.",
|
||||
"empty_column.bookmarked_statuses": "Dar neturi nė vienos įrašo su žyma. Kai vieną žymų pridėsi prie įrašo, jis bus rodomas čia.",
|
||||
"empty_column.community": "Vietinė laiko skalė yra tuščia. Parašyk ką nors viešai, kad pradėtum sąveikauti.",
|
||||
"empty_column.direct": "Dar neturi jokių privačių paminėjimų. Kai išsiųsi arba gausi vieną iš jų, jis bus rodomas čia.",
|
||||
"empty_column.disabled_feed": "Šis srautas buvo išjungtas jūsų serverio administratorių.",
|
||||
|
|
@ -354,8 +354,8 @@
|
|||
"empty_column.favourited_statuses": "Dar neturi mėgstamų įrašų. Kai vieną iš jų pamėgsi, jis bus rodomas čia.",
|
||||
"empty_column.favourites": "Šio įrašo dar niekas nepamėgo. Kai kas nors tai padarys, jie bus rodomi čia.",
|
||||
"empty_column.follow_requests": "Dar neturi jokių sekimo prašymų. Kai gausi tokį prašymą, jis bus rodomas čia.",
|
||||
"empty_column.followed_tags": "Dar neseki jokių saitažodžių. Kai tai padarysi, jie bus rodomi čia.",
|
||||
"empty_column.hashtag": "Nėra nieko šiame saitažodyje kol kas.",
|
||||
"empty_column.followed_tags": "Dar neseki jokių grotažymių. Kai tai padarysi, jos bus rodomos čia.",
|
||||
"empty_column.hashtag": "Šioje gratažymėje kol kas nieko nėra.",
|
||||
"empty_column.home": "Tavo pagrindinio laiko skalė tuščia. Sek daugiau žmonių, kad ją užpildytum.",
|
||||
"empty_column.list": "Šiame sąraše dar nieko nėra. Kai šio sąrašo nariai paskelbs naujus įrašus, jie bus rodomi čia.",
|
||||
"empty_column.mutes": "Dar nesi nutildęs (-usi) nė vieno naudotojo.",
|
||||
|
|
@ -373,7 +373,7 @@
|
|||
"explore.title": "Populiaru",
|
||||
"explore.trending_links": "Naujienos",
|
||||
"explore.trending_statuses": "Įrašai",
|
||||
"explore.trending_tags": "Saitažodžiai",
|
||||
"explore.trending_tags": "Grotažymės",
|
||||
"featured_carousel.current": "<sr>Įrašas</sr> {current, number} / {max, number}",
|
||||
"featured_carousel.header": "{count, plural, one {Iškeltas įrašas} few {Iškelti įrašai} many {Iškeltų įrašų} other {Iškelti įrašai}}",
|
||||
"featured_carousel.slide": "Įrašas {current, number} iš {max, number}",
|
||||
|
|
@ -417,7 +417,7 @@
|
|||
"follow_suggestions.similar_to_recently_followed_longer": "Panašūs į profilius, kuriuos neseniai seki",
|
||||
"follow_suggestions.view_all": "Peržiūrėti viską",
|
||||
"follow_suggestions.who_to_follow": "Ką sekti",
|
||||
"followed_tags": "Sekami saitažodžiai",
|
||||
"followed_tags": "Sekamos grotažymės",
|
||||
"footer.about": "Apie",
|
||||
"footer.about_this_server": "Apie",
|
||||
"footer.directory": "Profilių katalogas",
|
||||
|
|
@ -429,26 +429,26 @@
|
|||
"footer.terms_of_service": "Paslaugų sąlygos",
|
||||
"generic.saved": "Išsaugota",
|
||||
"getting_started.heading": "Kaip pradėti",
|
||||
"hashtag.admin_moderation": "Atverti prižiūrėjimo sąsają saitažodžiui #{name}",
|
||||
"hashtag.admin_moderation": "Atverti moderavimo langą #{name}",
|
||||
"hashtag.browse": "Naršyti įrašus su #{hashtag}",
|
||||
"hashtag.browse_from_account": "Naršyti @{name} įrašus su žyma #{hashtag}",
|
||||
"hashtag.browse_from_account": "Naršyti @{name} įrašus su grotažyme #{hashtag}",
|
||||
"hashtag.column_header.tag_mode.all": "ir {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "ar {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "be {additional}",
|
||||
"hashtag.column_settings.select.no_options_message": "Pasiūlymų nerasta.",
|
||||
"hashtag.column_settings.select.placeholder": "Įvesti saitažodžius…",
|
||||
"hashtag.column_settings.select.placeholder": "Įvesk grotažymes…",
|
||||
"hashtag.column_settings.tag_mode.all": "Visi šie",
|
||||
"hashtag.column_settings.tag_mode.any": "Bet kuris iš šių",
|
||||
"hashtag.column_settings.tag_mode.none": "Nė vienas iš šių",
|
||||
"hashtag.column_settings.tag_toggle": "Įtraukti papildomas šio stulpelio žymes",
|
||||
"hashtag.counter_by_accounts": "{count, plural, one {{counter} dalyvis} few {{counter} dalyviai} many {{counter} dalyvio} other {{counter} dalyvių}}",
|
||||
"hashtag.column_settings.tag_mode.any": "Bet kuris šių",
|
||||
"hashtag.column_settings.tag_mode.none": "Nei vienas šių",
|
||||
"hashtag.column_settings.tag_toggle": "Įtraukti papildomas žymas į šį stulpelį",
|
||||
"hashtag.counter_by_accounts": "{count, plural, one {{counter} dalyvis} few {{counter} dalyviai} many {{counter} dalyvių} other {{counter} dalyviai}}",
|
||||
"hashtag.counter_by_uses": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}}",
|
||||
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}} šiandien",
|
||||
"hashtag.feature": "Rodyti profilyje",
|
||||
"hashtag.follow": "Sekti saitažodį",
|
||||
"hashtag.follow": "Sekti grotažymę",
|
||||
"hashtag.mute": "Nutildyti žymą #{hashtag}",
|
||||
"hashtag.unfeature": "Neberodyti profilyje",
|
||||
"hashtag.unfollow": "Nebesekti saitažodį",
|
||||
"hashtag.unfollow": "Nebesekti grotažymės",
|
||||
"hashtags.and_other": "…ir {count, plural, one {# daugiau} few {# daugiau} many {# daugiau}other {# daugiau}}",
|
||||
"hints.profiles.followers_may_be_missing": "Sekėjai šiai profiliui gali būti nepateikti.",
|
||||
"hints.profiles.follows_may_be_missing": "Sekimai šiai profiliui gali būti nepateikti.",
|
||||
|
|
@ -489,7 +489,7 @@
|
|||
"intervals.full.minutes": "{number, plural, one {# minutė} few {# minutes} many {# minutės} other {# minučių}}",
|
||||
"keyboard_shortcuts.back": "Naršyti atgal",
|
||||
"keyboard_shortcuts.blocked": "Atidaryti užblokuotų naudotojų sąrašą",
|
||||
"keyboard_shortcuts.boost": "Pakelti įrašą",
|
||||
"keyboard_shortcuts.boost": "Dalintis įrašu",
|
||||
"keyboard_shortcuts.column": "Fokusuoti stulpelį",
|
||||
"keyboard_shortcuts.compose": "Fokusuoti rengykles teksto sritį",
|
||||
"keyboard_shortcuts.description": "Aprašymas",
|
||||
|
|
@ -587,7 +587,7 @@
|
|||
"navigation_bar.favourites": "Mėgstami",
|
||||
"navigation_bar.filters": "Nutildyti žodžiai",
|
||||
"navigation_bar.follow_requests": "Sekimo prašymai",
|
||||
"navigation_bar.followed_tags": "Sekami saitažodžiai",
|
||||
"navigation_bar.followed_tags": "Sekamos grotažymės",
|
||||
"navigation_bar.follows_and_followers": "Sekimai ir sekėjai",
|
||||
"navigation_bar.import_export": "Importas ir eksportas",
|
||||
"navigation_bar.lists": "Sąrašai",
|
||||
|
|
@ -604,7 +604,7 @@
|
|||
"navigation_bar.search_trends": "Paieška / Populiaru",
|
||||
"navigation_panel.collapse_followed_tags": "Sutraukti sekamų žymių meniu",
|
||||
"navigation_panel.collapse_lists": "Sutraukti sąrašo meniu",
|
||||
"navigation_panel.expand_followed_tags": "Išskleisti sekamų žymių meniu",
|
||||
"navigation_panel.expand_followed_tags": "Išskleisti sekamų grotažymių meniu",
|
||||
"navigation_panel.expand_lists": "Išskleisti sąrašo meniu",
|
||||
"not_signed_in_indicator.not_signed_in": "Norint pasiekti šį išteklį, reikia prisijungti.",
|
||||
"notification.admin.report": "{name} pranešė {target}",
|
||||
|
|
@ -635,16 +635,16 @@
|
|||
"notification.moderation_warning": "Gavai prižiūrėjimo įspėjimą",
|
||||
"notification.moderation_warning.action_delete_statuses": "Kai kurie tavo įrašai buvo pašalintos.",
|
||||
"notification.moderation_warning.action_disable": "Tavo paskyra buvo išjungta.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Kai kurie tavo įrašai buvo pažymėtos kaip jautrios.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Kai kurie tavo įrašai buvo pažymėti kaip turintys jautrią informaciją.",
|
||||
"notification.moderation_warning.action_none": "Tavo paskyra gavo prižiūrėjimo įspėjimą.",
|
||||
"notification.moderation_warning.action_sensitive": "Nuo šiol tavo įrašai bus pažymėti kaip jautrūs.",
|
||||
"notification.moderation_warning.action_sensitive": "Nuo šiol tavo įrašai bus pažymėti kaip su jautria informacija.",
|
||||
"notification.moderation_warning.action_silence": "Tavo paskyra buvo apribota.",
|
||||
"notification.moderation_warning.action_suspend": "Tavo paskyra buvo sustabdyta.",
|
||||
"notification.own_poll": "Tavo apklausa baigėsi",
|
||||
"notification.poll": "Baigėsi apklausa, kurioje balsavai",
|
||||
"notification.quoted_update": "{name} redagavo jūsų cituotą įrašą",
|
||||
"notification.reblog": "{name} pakėlė tavo įrašą",
|
||||
"notification.reblog.name_and_others_with_link": "{name} ir <a>{count, plural,one {dar kažkas} few {# kiti} other {# kitų}}</a> paryškino tavo įrašą",
|
||||
"notification.reblog": "{name} dalinosi tavo įrašu",
|
||||
"notification.reblog.name_and_others_with_link": "{name} ir <a>{count, plural,one {dar kažkas} few {# kiti} other {# kitų}}</a> pasidalino tavo įrašu",
|
||||
"notification.relationships_severance_event": "Prarasti sąryšiai su {name}",
|
||||
"notification.relationships_severance_event.account_suspension": "Administratorius iš {from} sustabdė {target}, todėl jūs nebegalėsite gauti jų naujienų ar bendrauti su jais.",
|
||||
"notification.relationships_severance_event.domain_block": "Administratorius iš {from} užblokavo {target}, įskaitant {followersCount} iš tavo sekėjų ir {followingCount, plural,one {# žmogų, kurį} few {# žmones, kuriuos} other {# žmonių, kuriuos}} seki.",
|
||||
|
|
@ -687,7 +687,7 @@
|
|||
"notifications.column_settings.poll": "Balsavimo rezultatai:",
|
||||
"notifications.column_settings.push": "Tiesioginiai pranešimai",
|
||||
"notifications.column_settings.quote": "Paminėjimai:",
|
||||
"notifications.column_settings.reblog": "Pakėlimai:",
|
||||
"notifications.column_settings.reblog": "Pasidalinimai:",
|
||||
"notifications.column_settings.show": "Rodyti stulpelyje",
|
||||
"notifications.column_settings.sound": "Paleisti garsą",
|
||||
"notifications.column_settings.status": "Nauji įrašai:",
|
||||
|
|
@ -695,7 +695,7 @@
|
|||
"notifications.column_settings.unread_notifications.highlight": "Paryškinti neperskaitytus pranešimus",
|
||||
"notifications.column_settings.update": "Redagavimai:",
|
||||
"notifications.filter.all": "Visi",
|
||||
"notifications.filter.boosts": "Pakėlimai",
|
||||
"notifications.filter.boosts": "Pasidalinimai",
|
||||
"notifications.filter.favourites": "Mėgstami",
|
||||
"notifications.filter.follows": "Sekimai",
|
||||
"notifications.filter.mentions": "Paminėjimai",
|
||||
|
|
@ -765,7 +765,7 @@
|
|||
"privacy.quote.anyone": "{visibility}, kiekvienas gali cituoti",
|
||||
"privacy.quote.disabled": "{visibility}, paminėjimai išjungti",
|
||||
"privacy.quote.limited": "{visibility}, paminėjimai apriboti",
|
||||
"privacy.unlisted.additional": "Tai veikia lygiai taip pat, kaip ir vieša, tik įrašas nebus rodomas tiesioginiuose srautuose, saitažodžiose, naršyme ar Mastodon paieškoje, net jei esi įtraukęs (-usi) visą paskyrą.",
|
||||
"privacy.unlisted.additional": "Tai veikia lygiai taip pat, kaip ir vieša, tik įrašas nebus rodomas tiesioginiuose srautuose, grotažymėse, naršyme ar Mastodon paieškoje, net jei esi įtraukęs (-usi) visą paskyrą.",
|
||||
"privacy.unlisted.long": "Paslėptas nuo „Mastodon“ paieškos rezultatų, tendencijų ir viešų įrašų sienų",
|
||||
"privacy.unlisted.short": "Tyliai vieša",
|
||||
"privacy_policy.last_updated": "Paskutinį kartą atnaujinta {date}",
|
||||
|
|
@ -866,9 +866,9 @@
|
|||
"search_popout.user": "naudotojas",
|
||||
"search_results.accounts": "Profiliai",
|
||||
"search_results.all": "Visi",
|
||||
"search_results.hashtags": "Saitažodžiai",
|
||||
"search_results.hashtags": "Grotažymės",
|
||||
"search_results.no_results": "Nėra rezultatų.",
|
||||
"search_results.no_search_yet": "Pabandykite ieškoti įrašų, profilių arba saitažodžių.",
|
||||
"search_results.no_search_yet": "Pabandykite ieškoti įrašų, profilių arba grotažymių.",
|
||||
"search_results.see_all": "Žiūrėti viską",
|
||||
"search_results.statuses": "Įrašai",
|
||||
"search_results.title": "Paieška užklausai „{q}“",
|
||||
|
|
@ -885,10 +885,10 @@
|
|||
"status.admin_account": "Atidaryti prižiūrėjimo sąsają @{name}",
|
||||
"status.admin_domain": "Atidaryti prižiūrėjimo sąsają {domain}",
|
||||
"status.admin_status": "Atidaryti šį įrašą prižiūrėjimo sąsajoje",
|
||||
"status.all_disabled": "Įrašo pakėlimai ir paminėjimai išjungti",
|
||||
"status.all_disabled": "Įrašo dalinimaisi ir paminėjimai išjungti",
|
||||
"status.block": "Blokuoti @{name}",
|
||||
"status.bookmark": "Pridėti į žymės",
|
||||
"status.cancel_reblog_private": "Nebepasidalinti",
|
||||
"status.bookmark": "Žymė",
|
||||
"status.cancel_reblog_private": "Nesidalinti",
|
||||
"status.cannot_quote": "Jums neleidžiama paminėti šio įrašo",
|
||||
"status.cannot_reblog": "Šis įrašas negali būti pakeltas.",
|
||||
"status.contains_quote": "Turi citatą",
|
||||
|
|
@ -910,6 +910,7 @@
|
|||
"status.edited_x_times": "Redaguota {count, plural, one {{count} kartą} few {{count} kartus} many {{count} karto} other {{count} kartų}}",
|
||||
"status.embed": "Gaukite įterpimo kodą",
|
||||
"status.favourite": "Pamėgti",
|
||||
"status.favourites_count": "{count, plural, one {{counter} patiko} few {{counter} patiko} many {{counter} patiko} other {{counter} patiko}}",
|
||||
"status.filter": "Filtruoti šį įrašą",
|
||||
"status.history.created": "{name} sukurta {date}",
|
||||
"status.history.edited": "{name} redaguota {date}",
|
||||
|
|
@ -944,11 +945,14 @@
|
|||
"status.quotes.empty": "Šio įrašo dar niekas nepaminėjo. Kai kas nors tai padarys, jie bus rodomi čia.",
|
||||
"status.quotes.local_other_disclaimer": "Autoriaus atmesti įrašo paminėjimai nebus rodomi.",
|
||||
"status.quotes.remote_other_disclaimer": "Čia bus rodoma tik paminėjimai iš {domain}. Autoriaus atmesti įrašo paminėjimai nebus rodomi.",
|
||||
"status.quotes_count": "{count, plural, one {{counter} paminėjimas} few {{counter} paminėjimai} many {{counter} paminėjimai} other {{counter} paminėjimai}}",
|
||||
"status.read_more": "Skaityti daugiau",
|
||||
"status.reblog": "Pakelti",
|
||||
"status.reblog_or_quote": "Paryškinti arba cituoti",
|
||||
"status.reblogged_by": "{name} pakėlė",
|
||||
"status.reblogs.empty": "Šio įrašo dar niekas nepakėlė. Kai kas nors tai padarys, jie bus rodomi čia.",
|
||||
"status.reblog": "Dalintis",
|
||||
"status.reblog_or_quote": "Dalintis arba cituoti",
|
||||
"status.reblog_private": "Vėl pasidalinkite su savo sekėjais",
|
||||
"status.reblogged_by": "{name} pasidalino",
|
||||
"status.reblogs.empty": "Šiuo įrašu dar niekas nesidalino. Kai kas nors tai padarys, jie bus rodomi čia.",
|
||||
"status.reblogs_count": "{count, plural, one {{counter} pasidalinimas} few {{counter} pasidalinimai} many {{counter} pasidalinimų} other {{counter} pasidalinimai}}",
|
||||
"status.redraft": "Ištrinti ir parengti iš naujo",
|
||||
"status.remove_bookmark": "Pašalinti žymę",
|
||||
"status.remove_favourite": "Šalinti iš mėgstamų",
|
||||
|
|
@ -975,6 +979,7 @@
|
|||
"subscribed_languages.save": "Išsaugoti pakeitimus",
|
||||
"subscribed_languages.target": "Keisti prenumeruojamas kalbas {target}",
|
||||
"tabs_bar.home": "Pagrindinis",
|
||||
"tabs_bar.menu": "Meniu",
|
||||
"tabs_bar.notifications": "Pranešimai",
|
||||
"tabs_bar.publish": "Naujas įrašas",
|
||||
"tabs_bar.search": "Paieška",
|
||||
|
|
@ -1023,10 +1028,13 @@
|
|||
"visibility_modal.button_title": "Nustatyti matomumą",
|
||||
"visibility_modal.direct_quote_warning.text": "Jei išsaugosite dabartinius nustatymus, įterpta citata bus konvertuota į nuorodą.",
|
||||
"visibility_modal.direct_quote_warning.title": "Cituojami įrašai negali būti įterpiami į privačius paminėjimus",
|
||||
"visibility_modal.header": "Matomumas ir sąveika",
|
||||
"visibility_modal.helper.direct_quoting": "Privatūs paminėjimai, parašyti platformoje „Mastodon“, negali būti cituojami kitų.",
|
||||
"visibility_modal.helper.privacy_editing": "Matomumo nustatymai negali būti keičiami po to, kai įrašas yra paskelbtas.",
|
||||
"visibility_modal.helper.privacy_private_self_quote": "Privačių įrašų paminėjimai negali būti skelbiami viešai.",
|
||||
"visibility_modal.helper.private_quoting": "Tik sekėjams skirti įrašai, parašyti platformoje „Mastodon“, negali būti cituojami kitų.",
|
||||
"visibility_modal.helper.unlisted_quoting": "Kai žmonės jus cituos, jų įrašai taip pat bus paslėpti iš populiariausių naujienų srauto.",
|
||||
"visibility_modal.instructions": "Kontroliuokite, kas gali bendrauti su šiuo įrašu. Taip pat galite taikyti nustatymus visiems būsimiems įrašams, pereidami į <link>Preferences > Posting defaults</link>.",
|
||||
"visibility_modal.privacy_label": "Matomumas",
|
||||
"visibility_modal.quote_followers": "Tik sekėjai",
|
||||
"visibility_modal.quote_label": "Kas gali cituoti",
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@
|
|||
"alt_text_modal.describe_for_people_with_visual_impairments": "請替看有困難ê敘述tsit ê內容…",
|
||||
"alt_text_modal.done": "做好ah",
|
||||
"announcement.announcement": "公告",
|
||||
"annual_report.announcement.action_build": "建立我ê Wrapstodon",
|
||||
"annual_report.announcement.action_view": "看我ê Wrapstodon",
|
||||
"annual_report.announcement.description": "發現其他關係lí佇最近tsi̍t年參與Mastodon ê狀況。",
|
||||
"annual_report.announcement.title": "Wrapstodon {year} 已經kàu ah",
|
||||
"annual_report.summary.archetype.booster": "追求趣味ê",
|
||||
"annual_report.summary.archetype.lurker": "有讀無PO ê",
|
||||
"annual_report.summary.archetype.oracle": "先知",
|
||||
|
|
@ -131,6 +135,7 @@
|
|||
"annual_report.summary.new_posts.new_posts": "新ê PO文",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Tse 予lí變做 {domain} ê用戶ê </topLabel><percentage></percentage><bottomLabel></bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Gún bē kā Bernie講。",
|
||||
"annual_report.summary.share_message": "我得著 {archetype} ê典型!",
|
||||
"annual_report.summary.thanks": "多謝成做Mastodon ê成員!",
|
||||
"attachments_list.unprocessed": "(Iáu bē處理)",
|
||||
"audio.hide": "Tshàng聲音",
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@
|
|||
"annual_report.summary.new_posts.new_posts": "nieuwe berichten",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Hiermee behoor je tot de top</topLabel><percentage></percentage><bottomLabel> van {domain}.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "We zullen Bernie niets vertellen.",
|
||||
"annual_report.summary.share_message": "Ik heb het archetype {archetype}!",
|
||||
"annual_report.summary.thanks": "Bedankt dat je deel uitmaakt van Mastodon!",
|
||||
"attachments_list.unprocessed": "(niet verwerkt)",
|
||||
"audio.hide": "Audio verbergen",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
"account.edit_profile_short": "แก้ไข",
|
||||
"account.enable_notifications": "แจ้งเตือนฉันเมื่อ @{name} โพสต์",
|
||||
"account.endorse": "แสดงในโปรไฟล์",
|
||||
"account.familiar_followers_one": "ติดตามโดย {name1}",
|
||||
"account.familiar_followers_two": "ติดตามโดย {name1} และ {name2}",
|
||||
"account.featured": "น่าสนใจ",
|
||||
"account.featured.accounts": "โปรไฟล์",
|
||||
"account.featured.hashtags": "แฮชแท็ก",
|
||||
|
|
@ -65,6 +67,7 @@
|
|||
"account.mute_short": "ซ่อน",
|
||||
"account.muted": "ซ่อนอยู่",
|
||||
"account.muting": "กำลังซ่อน",
|
||||
"account.mutual": "คุณติดตามกันและกัน",
|
||||
"account.no_bio": "ไม่ได้ให้คำอธิบาย",
|
||||
"account.open_original_page": "เปิดหน้าดั้งเดิม",
|
||||
"account.posts": "โพสต์",
|
||||
|
|
@ -232,6 +235,7 @@
|
|||
"confirmations.missing_alt_text.secondary": "โพสต์ต่อไป",
|
||||
"confirmations.missing_alt_text.title": "เพิ่มข้อความแสดงแทน?",
|
||||
"confirmations.mute.confirm": "ซ่อน",
|
||||
"confirmations.private_quote_notify.confirm": "เผยแพร่โพสต์",
|
||||
"confirmations.quiet_post_quote_info.dismiss": "ไม่ต้องเตือนฉันอีก",
|
||||
"confirmations.quiet_post_quote_info.got_it": "เข้าใจแล้ว",
|
||||
"confirmations.redraft.confirm": "ลบแล้วร่างใหม่",
|
||||
|
|
@ -381,6 +385,7 @@
|
|||
"follow_suggestions.who_to_follow": "ติดตามใครดี",
|
||||
"followed_tags": "แฮชแท็กที่ติดตาม",
|
||||
"footer.about": "เกี่ยวกับ",
|
||||
"footer.about_this_server": "เกี่ยวกับ",
|
||||
"footer.directory": "ไดเรกทอรีโปรไฟล์",
|
||||
"footer.get_app": "รับแอป",
|
||||
"footer.keyboard_shortcuts": "แป้นพิมพ์ลัด",
|
||||
|
|
@ -417,6 +422,7 @@
|
|||
"hints.profiles.see_more_followers": "ดูผู้ติดตามเพิ่มเติมใน {domain}",
|
||||
"hints.profiles.see_more_follows": "ดูการติดตามเพิ่มเติมใน {domain}",
|
||||
"hints.profiles.see_more_posts": "ดูโพสต์เพิ่มเติมใน {domain}",
|
||||
"home.column_settings.show_quotes": "แสดงการอ้างอิง",
|
||||
"home.column_settings.show_reblogs": "แสดงการดัน",
|
||||
"home.column_settings.show_replies": "แสดงการตอบกลับ",
|
||||
"home.hide_announcements": "ซ่อนประกาศ",
|
||||
|
|
@ -461,6 +467,7 @@
|
|||
"keyboard_shortcuts.home": "เปิดเส้นเวลาหน้าแรก",
|
||||
"keyboard_shortcuts.hotkey": "ปุ่มลัด",
|
||||
"keyboard_shortcuts.legend": "แสดงคำอธิบายนี้",
|
||||
"keyboard_shortcuts.load_more": "โฟกัสปุ่ม \"โหลดเพิ่มเติม\"",
|
||||
"keyboard_shortcuts.local": "เปิดเส้นเวลาในเซิร์ฟเวอร์",
|
||||
"keyboard_shortcuts.mention": "กล่าวถึงผู้สร้าง",
|
||||
"keyboard_shortcuts.muted": "เปิดรายการผู้ใช้ที่ซ่อนอยู่",
|
||||
|
|
@ -469,6 +476,7 @@
|
|||
"keyboard_shortcuts.open_media": "เปิดสื่อ",
|
||||
"keyboard_shortcuts.pinned": "เปิดรายการโพสต์ที่ปักหมุด",
|
||||
"keyboard_shortcuts.profile": "เปิดโปรไฟล์ของผู้สร้าง",
|
||||
"keyboard_shortcuts.quote": "อ้างอิงโพสต์",
|
||||
"keyboard_shortcuts.reply": "ตอบกลับโพสต์",
|
||||
"keyboard_shortcuts.requests": "เปิดรายการคำขอติดตาม",
|
||||
"keyboard_shortcuts.search": "โฟกัสแถบค้นหา",
|
||||
|
|
@ -546,6 +554,8 @@
|
|||
"navigation_bar.follows_and_followers": "การติดตามและผู้ติดตาม",
|
||||
"navigation_bar.import_export": "การนำเข้าและการส่งออก",
|
||||
"navigation_bar.lists": "รายการ",
|
||||
"navigation_bar.live_feed_local": "ฟีดสด (ในเซิร์ฟเวอร์)",
|
||||
"navigation_bar.live_feed_public": "ฟีดสด (สาธารณะ)",
|
||||
"navigation_bar.logout": "ออกจากระบบ",
|
||||
"navigation_bar.moderation": "การกลั่นกรอง",
|
||||
"navigation_bar.more": "เพิ่มเติม",
|
||||
|
|
@ -554,6 +564,7 @@
|
|||
"navigation_bar.preferences": "การกำหนดลักษณะ",
|
||||
"navigation_bar.privacy_and_reach": "ความเป็นส่วนตัวและการเข้าถึง",
|
||||
"navigation_bar.search": "ค้นหา",
|
||||
"navigation_bar.search_trends": "ค้นหา / กำลังนิยม",
|
||||
"navigation_panel.collapse_lists": "ยุบเมนูรายการ",
|
||||
"navigation_panel.expand_lists": "ขยายเมนูรายการ",
|
||||
"not_signed_in_indicator.not_signed_in": "คุณจำเป็นต้องเข้าสู่ระบบเพื่อเข้าถึงทรัพยากรนี้",
|
||||
|
|
@ -576,6 +587,7 @@
|
|||
"notification.label.mention": "การกล่าวถึง",
|
||||
"notification.label.private_mention": "การกล่าวถึงแบบส่วนตัว",
|
||||
"notification.label.private_reply": "การตอบกลับแบบส่วนตัว",
|
||||
"notification.label.quote": "{name} ได้อ้างอิงโพสต์ของคุณ",
|
||||
"notification.label.reply": "การตอบกลับ",
|
||||
"notification.mention": "การกล่าวถึง",
|
||||
"notification.mentioned_you": "{name} ได้กล่าวถึงคุณ",
|
||||
|
|
@ -590,6 +602,7 @@
|
|||
"notification.moderation_warning.action_suspend": "ระงับบัญชีของคุณแล้ว",
|
||||
"notification.own_poll": "การสำรวจความคิดเห็นของคุณได้สิ้นสุดแล้ว",
|
||||
"notification.poll": "การสำรวจความคิดเห็นที่คุณได้ลงคะแนนได้สิ้นสุดแล้ว",
|
||||
"notification.quoted_update": "{name} ได้แก้ไขโพสต์ที่คุณได้อ้างอิง",
|
||||
"notification.reblog": "{name} ได้ดันโพสต์ของคุณ",
|
||||
"notification.reblog.name_and_others_with_link": "{name} และ <a>{count, plural, other {# อื่น ๆ}}</a> ได้ดันโพสต์ของคุณ",
|
||||
"notification.relationships_severance_event": "สูญเสียการเชื่อมต่อกับ {name}",
|
||||
|
|
@ -633,6 +646,7 @@
|
|||
"notifications.column_settings.mention": "การกล่าวถึง:",
|
||||
"notifications.column_settings.poll": "ผลลัพธ์การสำรวจความคิดเห็น:",
|
||||
"notifications.column_settings.push": "การแจ้งเตือนแบบผลัก",
|
||||
"notifications.column_settings.quote": "การอ้างอิง:",
|
||||
"notifications.column_settings.reblog": "การดัน:",
|
||||
"notifications.column_settings.show": "แสดงในคอลัมน์",
|
||||
"notifications.column_settings.sound": "เล่นเสียง",
|
||||
|
|
@ -708,7 +722,11 @@
|
|||
"privacy.private.short": "ผู้ติดตาม",
|
||||
"privacy.public.long": "ใครก็ตามที่อยู่ในและนอก Mastodon",
|
||||
"privacy.public.short": "สาธารณะ",
|
||||
"privacy.quote.anyone": "{visibility}, ใครก็ตามสามารถอ้างอิง",
|
||||
"privacy.quote.disabled": "{visibility}, ปิดใช้งานการอ้างอิงแล้ว",
|
||||
"privacy.quote.limited": "{visibility}, จำกัดการอ้างอิงอยู่",
|
||||
"privacy.unlisted.additional": "สิ่งนี้ทำงานเหมือนกับสาธารณะทุกประการ ยกเว้นโพสต์จะไม่ปรากฏในฟีดสดหรือแฮชแท็ก, การสำรวจ หรือการค้นหา Mastodon แม้ว่าคุณได้เลือกรับทั่วทั้งบัญชีก็ตาม",
|
||||
"privacy.unlisted.long": "ซ่อนจากผลลัพธ์การค้นหา, กำลังนิยม และเส้นเวลาสาธารณะของ Mastodon",
|
||||
"privacy.unlisted.short": "สาธารณะแบบเงียบ",
|
||||
"privacy_policy.last_updated": "อัปเดตล่าสุดเมื่อ {date}",
|
||||
"privacy_policy.title": "นโยบายความเป็นส่วนตัว",
|
||||
|
|
@ -849,6 +867,8 @@
|
|||
"status.mute_conversation": "ซ่อนการสนทนา",
|
||||
"status.open": "ขยายโพสต์นี้",
|
||||
"status.pin": "ปักหมุดในโปรไฟล์",
|
||||
"status.quote_error.limited_account_hint.action": "แสดงต่อไป",
|
||||
"status.quote_post_author": "อ้างอิงโพสต์โดย @{name}",
|
||||
"status.read_more": "อ่านเพิ่มเติม",
|
||||
"status.reblog": "ดัน",
|
||||
"status.reblogged_by": "{name} ได้ดัน",
|
||||
|
|
@ -916,5 +936,11 @@
|
|||
"video.pause": "หยุดชั่วคราว",
|
||||
"video.play": "เล่น",
|
||||
"video.unmute": "เลิกปิดเสียง",
|
||||
"visibility_modal.button_title": "ตั้งการมองเห็น",
|
||||
"visibility_modal.header": "การมองเห็นและการโต้ตอบ",
|
||||
"visibility_modal.privacy_label": "การมองเห็น",
|
||||
"visibility_modal.quote_followers": "ผู้ติดตามเท่านั้น",
|
||||
"visibility_modal.quote_nobody": "แค่ฉัน",
|
||||
"visibility_modal.quote_public": "ใครก็ตาม",
|
||||
"visibility_modal.save": "บันทึก"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,8 +319,8 @@
|
|||
"domain_pill.your_handle": "您的帳號:",
|
||||
"domain_pill.your_server": "您數位世界的家,您所有的嘟文都在這裡。不喜歡這台伺服器嗎?您能隨時搬家至其他伺服器並且仍保有您的跟隨者。",
|
||||
"domain_pill.your_username": "您於您的伺服器中獨一無二的識別。於不同的伺服器上可能找到具有相同帳號的使用者。",
|
||||
"dropdown.empty": "選項",
|
||||
"embed.instructions": "若您欲於您的網站嵌入此嘟文,請複製以下程式碼。",
|
||||
"dropdown.empty": "請選擇一個選項",
|
||||
"embed.instructions": "請複製以下程式碼以於您的網站嵌入此嘟文。",
|
||||
"embed.preview": "它將顯示成這樣:",
|
||||
"emoji_button.activity": "活動",
|
||||
"emoji_button.clear": "清除",
|
||||
|
|
@ -344,7 +344,7 @@
|
|||
"empty_column.account_suspended": "帳號已被停權",
|
||||
"empty_column.account_timeline": "這裡還沒有嘟文!",
|
||||
"empty_column.account_unavailable": "無法取得個人檔案",
|
||||
"empty_column.blocks": "您還沒有封鎖任何使用者。",
|
||||
"empty_column.blocks": "您尚未封鎖任何使用者。",
|
||||
"empty_column.bookmarked_statuses": "您還沒有新增任何書籤。當您新增書籤時,它將於此顯示。",
|
||||
"empty_column.community": "本站時間軸是空的。快公開嘟些文搶頭香啊!",
|
||||
"empty_column.direct": "您還沒有收到任何私訊。當您私訊別人或收到私訊時,它將於此顯示。",
|
||||
|
|
@ -356,12 +356,12 @@
|
|||
"empty_column.follow_requests": "您還沒有收到任何跟隨請求。當您收到的跟隨請求時,它將於此顯示。",
|
||||
"empty_column.followed_tags": "您還沒有跟隨任何主題標籤。當您跟隨主題標籤時,它們將於此顯示。",
|
||||
"empty_column.hashtag": "這個主題標籤下什麼也沒有。",
|
||||
"empty_column.home": "您的首頁時間軸是空的!跟隨更多人來將它填滿吧!",
|
||||
"empty_column.home": "您的首頁時間軸是空的!跟隨更多人將它填滿吧!",
|
||||
"empty_column.list": "這份列表下什麼也沒有。當此列表的成員嘟出新的嘟文時,它們將顯示於此。",
|
||||
"empty_column.mutes": "您尚未靜音任何使用者。",
|
||||
"empty_column.notification_requests": "清空啦!已經沒有任何推播通知。當您收到新推播通知時,它們將依照您的設定於此顯示。",
|
||||
"empty_column.notifications": "您還沒有收到任何推播通知,當您與別人開始互動時,它將於此顯示。",
|
||||
"empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或者跟隨其他伺服器的使用者後,就會有嘟文出現了",
|
||||
"empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或著自己跟隨其他伺服器的使用者後就會有嘟文出現了",
|
||||
"error.no_hashtag_feed_access": "加入或登入 Mastodon 以檢視與跟隨此主題標籤。",
|
||||
"error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,無法正常顯示此頁面。",
|
||||
"error.unexpected_crash.explanation_addons": "此頁面無法被正常顯示,這可能是由瀏覽器附加元件或網頁自動翻譯工具造成的。",
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ export interface TimeSeriesMonth {
|
|||
}
|
||||
|
||||
export interface TopStatuses {
|
||||
by_reblogs: number;
|
||||
by_favourites: number;
|
||||
by_replies: number;
|
||||
by_reblogs: string;
|
||||
by_favourites: string;
|
||||
by_replies: string;
|
||||
}
|
||||
|
||||
export type Archetype =
|
||||
|
|
@ -55,5 +55,6 @@ export type AnnualReport = {
|
|||
schema_version: 2;
|
||||
data: AnnualReportV2;
|
||||
share_url: string | null;
|
||||
account_id: string;
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import type {
|
|||
import type { ApiReportJSON } from 'mastodon/api_types/reports';
|
||||
|
||||
// Maximum number of avatars displayed in a notification group
|
||||
// This corresponds to the max lenght of `group.sampleAccountIds`
|
||||
// This corresponds to the max length of `group.sampleAccountIds`
|
||||
export const NOTIFICATIONS_GROUP_MAX_AVATARS = 8;
|
||||
|
||||
interface BaseNotificationGroup
|
||||
|
|
|
|||
|
|
@ -363,7 +363,9 @@ export const composeReducer = (state = initialState, action) => {
|
|||
|
||||
switch(action.type) {
|
||||
case STORE_HYDRATE:
|
||||
if (action.state.get('compose'))
|
||||
return hydrate(state, action.state.get('compose'));
|
||||
return state;
|
||||
case COMPOSE_MOUNT:
|
||||
return state
|
||||
.set('mounted', state.get('mounted') + 1)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
importFetchedStatuses,
|
||||
} from '@/mastodon/actions/importer';
|
||||
import { insertIntoTimeline } from '@/mastodon/actions/timelines';
|
||||
import { timelineDelete } from '@/mastodon/actions/timelines_typed';
|
||||
import type { ApiAnnualReportState } from '@/mastodon/api/annual_report';
|
||||
import {
|
||||
apiGetAnnualReport,
|
||||
|
|
@ -78,6 +79,25 @@ export const checkAnnualReport = createAppThunk(
|
|||
},
|
||||
);
|
||||
|
||||
export const reinsertAnnualReport = createAppThunk(
|
||||
`${annualReportSlice.name}/reinsertAnnualReport`,
|
||||
(_arg: unknown, { dispatch, getState }) => {
|
||||
dispatch(
|
||||
timelineDelete({
|
||||
statusId: TIMELINE_WRAPSTODON,
|
||||
accountId: '',
|
||||
references: [],
|
||||
reblogOf: null,
|
||||
}),
|
||||
);
|
||||
const { state } = getState().annualReport;
|
||||
if (!state || state === 'ineligible') {
|
||||
return;
|
||||
}
|
||||
dispatch(insertIntoTimeline('home', TIMELINE_WRAPSTODON, 1));
|
||||
},
|
||||
);
|
||||
|
||||
const fetchReportState = createDataLoadingThunk(
|
||||
`${annualReportSlice.name}/fetchReportState`,
|
||||
async (_arg: unknown, { getState }) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
import { timelineDelete, isNonStatusId } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
import {
|
||||
blockAccountSuccess,
|
||||
|
|
@ -19,7 +19,6 @@ import {
|
|||
TIMELINE_MARK_AS_PARTIAL,
|
||||
TIMELINE_INSERT,
|
||||
TIMELINE_GAP,
|
||||
TIMELINE_SUGGESTIONS,
|
||||
disconnectTimeline,
|
||||
} from '../actions/timelines';
|
||||
import { compareId } from '../compare_id';
|
||||
|
|
@ -36,7 +35,6 @@ const initialTimeline = ImmutableMap({
|
|||
items: ImmutableList(),
|
||||
});
|
||||
|
||||
const isPlaceholder = value => value === TIMELINE_GAP || value === TIMELINE_SUGGESTIONS;
|
||||
|
||||
const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent, usePendingItems) => {
|
||||
// This method is pretty tricky because:
|
||||
|
|
@ -69,20 +67,20 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, is
|
|||
// First, find the furthest (if properly sorted, oldest) item in the timeline that is
|
||||
// newer than the oldest fetched one, as it's most likely that it delimits the gap.
|
||||
// Start the gap *after* that item.
|
||||
const lastIndex = oldIds.findLastIndex(id => !isPlaceholder(id) && compareId(id, newIds.last()) >= 0) + 1;
|
||||
const lastIndex = oldIds.findLastIndex(id => !isNonStatusId(id) && compareId(id, newIds.last()) >= 0) + 1;
|
||||
|
||||
// Then, try to find the furthest (if properly sorted, oldest) item in the timeline that
|
||||
// is newer than the most recent fetched one, as it delimits a section comprised of only
|
||||
// items older or within `newIds` (or that were deleted from the server, so should be removed
|
||||
// anyway).
|
||||
// Stop the gap *after* that item.
|
||||
const firstIndex = oldIds.take(lastIndex).findLastIndex(id => !isPlaceholder(id) && compareId(id, newIds.first()) > 0) + 1;
|
||||
const firstIndex = oldIds.take(lastIndex).findLastIndex(id => !isNonStatusId(id) && compareId(id, newIds.first()) > 0) + 1;
|
||||
|
||||
let insertedIds = ImmutableOrderedSet(newIds).withMutations(insertedIds => {
|
||||
// It is possible, though unlikely, that the slice we are replacing contains items older
|
||||
// than the elements we got from the API. Get them and add them back at the back of the
|
||||
// slice.
|
||||
const olderIds = oldIds.slice(firstIndex, lastIndex).filter(id => !isPlaceholder(id) && compareId(id, newIds.last()) < 0);
|
||||
const olderIds = oldIds.slice(firstIndex, lastIndex).filter(id => !isNonStatusId(id) && compareId(id, newIds.last()) < 0);
|
||||
insertedIds.union(olderIds);
|
||||
|
||||
// Make sure we aren't inserting duplicates
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
@use 'mastodon/polls';
|
||||
@use 'mastodon/modal';
|
||||
@use 'mastodon/emoji_picker';
|
||||
@use 'mastodon/annual_reports';
|
||||
@use 'mastodon/about';
|
||||
@use 'mastodon/tables';
|
||||
@use 'mastodon/admin';
|
||||
|
|
|
|||
|
|
@ -1,342 +0,0 @@
|
|||
@use 'variables' as *;
|
||||
|
||||
:root {
|
||||
--indigo-1: #17063b;
|
||||
--indigo-2: #2f0c7a;
|
||||
--indigo-3: #562cfc;
|
||||
--indigo-5: #858afa;
|
||||
--indigo-6: #cccfff;
|
||||
--lime: #baff3b;
|
||||
--goldenrod-2: #ffc954;
|
||||
}
|
||||
|
||||
.annual-report {
|
||||
flex: 0 0 auto;
|
||||
background: var(--indigo-1);
|
||||
padding: 24px;
|
||||
|
||||
&__header {
|
||||
margin-bottom: 16px;
|
||||
|
||||
h1 {
|
||||
font-size: 25px;
|
||||
font-weight: 600;
|
||||
line-height: 30px;
|
||||
color: var(--lime);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
color: var(--indigo-6);
|
||||
}
|
||||
}
|
||||
|
||||
&__bento {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
|
||||
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto) minmax(
|
||||
0,
|
||||
auto
|
||||
);
|
||||
|
||||
&__box {
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--indigo-2);
|
||||
color: var(--indigo-5);
|
||||
}
|
||||
}
|
||||
|
||||
&__summary {
|
||||
&__most-boosted-post {
|
||||
grid-column: span 2;
|
||||
grid-row: span 2;
|
||||
padding: 0;
|
||||
|
||||
.status__content,
|
||||
.content-warning {
|
||||
color: var(--indigo-6);
|
||||
}
|
||||
|
||||
.detailed-status {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.content-warning {
|
||||
border: 0;
|
||||
background: var(--indigo-1);
|
||||
|
||||
.link-button {
|
||||
color: var(--indigo-5);
|
||||
}
|
||||
}
|
||||
|
||||
.detailed-status__meta__line {
|
||||
border-bottom-color: var(--indigo-3);
|
||||
}
|
||||
|
||||
.detailed-status__meta {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.detailed-status__meta,
|
||||
.poll__footer,
|
||||
.poll__link,
|
||||
.detailed-status .logo,
|
||||
.detailed-status__display-name {
|
||||
color: var(--indigo-5);
|
||||
}
|
||||
|
||||
.detailed-status__meta .animated-number,
|
||||
.detailed-status__display-name strong {
|
||||
color: var(--indigo-6);
|
||||
}
|
||||
|
||||
.poll__chart {
|
||||
background-color: var(--indigo-3);
|
||||
|
||||
&.leading {
|
||||
background-color: var(--goldenrod-2);
|
||||
}
|
||||
}
|
||||
|
||||
.status-card,
|
||||
.hashtag-bar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__followers {
|
||||
grid-column: span 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding-block-start: 24px;
|
||||
padding-block-end: 24px;
|
||||
|
||||
--sparkline-gradient-top: rgba(86, 44, 252, 50%);
|
||||
--sparkline-gradient-bottom: rgba(86, 44, 252, 0%);
|
||||
|
||||
&__foreground {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__number {
|
||||
font-size: 31px;
|
||||
font-weight: 600;
|
||||
line-height: 37px;
|
||||
color: var(--lime);
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 17px;
|
||||
color: var(--indigo-6);
|
||||
}
|
||||
|
||||
&__footnote {
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
inset-inline-end: 0;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
height: 70%;
|
||||
width: auto;
|
||||
|
||||
path:first-child {
|
||||
fill: url('#gradient') !important;
|
||||
fill-opacity: 1 !important;
|
||||
}
|
||||
|
||||
path:last-child {
|
||||
stroke: var(--color-graph-primary-stroke) !important;
|
||||
fill: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__archetype {
|
||||
grid-column: span 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
gap: 8px;
|
||||
padding: 0;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
padding: 16px;
|
||||
padding-bottom: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
font-weight: 600;
|
||||
color: var(--lime);
|
||||
}
|
||||
}
|
||||
|
||||
&__most-used-app {
|
||||
grid-column: span 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&__label {
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
font-weight: 600;
|
||||
color: var(--indigo-6);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
font-weight: 600;
|
||||
color: var(--goldenrod-2);
|
||||
}
|
||||
}
|
||||
|
||||
&__percentile {
|
||||
grid-row: span 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
text-wrap: balance;
|
||||
padding: 16px 8px;
|
||||
|
||||
&__label {
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
&__number {
|
||||
font-size: 54px;
|
||||
font-weight: 600;
|
||||
line-height: 73px;
|
||||
color: var(--goldenrod-2);
|
||||
}
|
||||
|
||||
&__footnote {
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
&__new-posts {
|
||||
grid-column: span 2;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&__label {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
color: var(--indigo-6);
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__number {
|
||||
font-size: 76px;
|
||||
font-weight: 600;
|
||||
line-height: 91px;
|
||||
color: var(--goldenrod-2);
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
inset-inline-start: -7px;
|
||||
top: -4px;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__most-used-hashtag {
|
||||
grid-column: span 2;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
|
||||
&__hashtag {
|
||||
font-size: 42px;
|
||||
font-weight: 600;
|
||||
line-height: 58px;
|
||||
color: var(--indigo-6);
|
||||
margin-inline-start: -100%;
|
||||
margin-inline-end: -100%;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.annual-report-modal {
|
||||
max-width: 600px;
|
||||
background: var(--indigo-1);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
|
||||
.loading-indicator .circular-progress {
|
||||
color: var(--lime);
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
border-bottom: 0;
|
||||
border-radius: 16px 16px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-group--annual-report {
|
||||
.notification-group__icon {
|
||||
color: var(--lime);
|
||||
}
|
||||
|
||||
.notification-group__main .link-button {
|
||||
font-weight: 500;
|
||||
color: var(--lime);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
html {
|
||||
@include base.palette;
|
||||
|
||||
&[data-user-theme='system'] {
|
||||
&:where([data-user-theme='system']) {
|
||||
color-scheme: dark light;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
import { Map as ImmutableMap, List } from 'immutable';
|
||||
|
||||
import type { ApiRelationshipJSON } from '@/mastodon/api_types/relationships';
|
||||
import type { ApiStatusJSON } from '@/mastodon/api_types/statuses';
|
||||
|
|
@ -7,6 +7,7 @@ import type {
|
|||
UnicodeEmojiData,
|
||||
} from '@/mastodon/features/emoji/types';
|
||||
import { createAccountFromServerJSON } from '@/mastodon/models/account';
|
||||
import type { AnnualReport } from '@/mastodon/models/annual_report';
|
||||
import type { Status } from '@/mastodon/models/status';
|
||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
||||
|
||||
|
|
@ -75,16 +76,18 @@ export const statusFactory: FactoryFunction<ApiStatusJSON> = ({
|
|||
mentions: [],
|
||||
tags: [],
|
||||
emojis: [],
|
||||
content: '<p>This is a test status.</p>',
|
||||
contentHtml: '<p>This is a test status.</p>',
|
||||
...data,
|
||||
});
|
||||
|
||||
export const statusFactoryState = (
|
||||
options: FactoryOptions<ApiStatusJSON> = {},
|
||||
) =>
|
||||
ImmutableMap<string, unknown>(
|
||||
statusFactory(options) as unknown as Record<string, unknown>,
|
||||
) as unknown as Status;
|
||||
ImmutableMap<string, unknown>({
|
||||
...(statusFactory(options) as unknown as Record<string, unknown>),
|
||||
account: options.account?.id ?? '1',
|
||||
tags: List(options.tags),
|
||||
}) as unknown as Status;
|
||||
|
||||
export const relationshipsFactory: FactoryFunction<ApiRelationshipJSON> = ({
|
||||
id,
|
||||
|
|
@ -130,3 +133,119 @@ export function customEmojiFactory(
|
|||
...data,
|
||||
};
|
||||
}
|
||||
|
||||
interface AnnualReportState {
|
||||
state: 'available';
|
||||
report: AnnualReport;
|
||||
}
|
||||
|
||||
interface AnnualReportFactoryOptions {
|
||||
account_id?: string;
|
||||
status_id?: string;
|
||||
archetype?: AnnualReport['data']['archetype'];
|
||||
year?: number;
|
||||
top_hashtag?: AnnualReport['data']['top_hashtags'][0];
|
||||
without_posts?: boolean;
|
||||
}
|
||||
|
||||
export function annualReportFactory({
|
||||
account_id = '1',
|
||||
status_id = '1',
|
||||
archetype = 'lurker',
|
||||
year,
|
||||
top_hashtag,
|
||||
without_posts = false,
|
||||
}: AnnualReportFactoryOptions = {}): AnnualReportState {
|
||||
return {
|
||||
state: 'available',
|
||||
report: {
|
||||
schema_version: 2,
|
||||
share_url: '#',
|
||||
account_id,
|
||||
year: year ?? 2025,
|
||||
data: {
|
||||
archetype,
|
||||
time_series: [
|
||||
{
|
||||
month: 1,
|
||||
statuses: 0,
|
||||
followers: 0,
|
||||
following: 0,
|
||||
},
|
||||
{
|
||||
month: 2,
|
||||
statuses: 0,
|
||||
followers: 0,
|
||||
following: 0,
|
||||
},
|
||||
{
|
||||
month: 3,
|
||||
statuses: 0,
|
||||
followers: 0,
|
||||
following: 0,
|
||||
},
|
||||
{
|
||||
month: 4,
|
||||
statuses: 0,
|
||||
followers: 0,
|
||||
following: 0,
|
||||
},
|
||||
{
|
||||
month: 5,
|
||||
statuses: without_posts ? 0 : 1,
|
||||
followers: 1,
|
||||
following: 3,
|
||||
},
|
||||
{
|
||||
month: 6,
|
||||
statuses: without_posts ? 0 : 7,
|
||||
followers: 1,
|
||||
following: 0,
|
||||
},
|
||||
{
|
||||
month: 7,
|
||||
statuses: without_posts ? 0 : 2,
|
||||
followers: 0,
|
||||
following: 0,
|
||||
},
|
||||
{
|
||||
month: 8,
|
||||
statuses: without_posts ? 0 : 2,
|
||||
followers: 0,
|
||||
following: 0,
|
||||
},
|
||||
{
|
||||
month: 9,
|
||||
statuses: without_posts ? 0 : 11,
|
||||
followers: 0,
|
||||
following: 1,
|
||||
},
|
||||
{
|
||||
month: 10,
|
||||
statuses: without_posts ? 0 : 12,
|
||||
followers: 0,
|
||||
following: 1,
|
||||
},
|
||||
{
|
||||
month: 11,
|
||||
statuses: without_posts ? 0 : 6,
|
||||
followers: 0,
|
||||
following: 1,
|
||||
},
|
||||
{
|
||||
month: 12,
|
||||
statuses: without_posts ? 0 : 4,
|
||||
followers: 0,
|
||||
following: 0,
|
||||
},
|
||||
],
|
||||
top_hashtags: top_hashtag ? [top_hashtag] : [],
|
||||
top_statuses: {
|
||||
by_reblogs: status_id,
|
||||
by_replies: status_id,
|
||||
by_favourites: status_id,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,29 +3,24 @@
|
|||
class AnnualReport::TimeSeries < AnnualReport::Source
|
||||
def generate
|
||||
{
|
||||
time_series: (1..12).map do |month|
|
||||
time_series: [
|
||||
{
|
||||
month: month,
|
||||
statuses: statuses_per_month[month] || 0,
|
||||
following: following_per_month[month] || 0,
|
||||
followers: followers_per_month[month] || 0,
|
||||
}
|
||||
end,
|
||||
month: 12,
|
||||
statuses: statuses_this_year,
|
||||
followers: followers_this_year,
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def statuses_per_month
|
||||
@statuses_per_month ||= report_statuses.group(:period).pluck(date_part_month.as('period'), Arel.star.count).to_h
|
||||
def statuses_this_year
|
||||
@statuses_this_year ||= report_statuses.count
|
||||
end
|
||||
|
||||
def following_per_month
|
||||
@following_per_month ||= annual_relationships_by_month(@account.active_relationships)
|
||||
end
|
||||
|
||||
def followers_per_month
|
||||
@followers_per_month ||= annual_relationships_by_month(@account.passive_relationships)
|
||||
def followers_this_year
|
||||
@followers_this_year ||= @account.passive_relationships.where(created_in_year, @year).count
|
||||
end
|
||||
|
||||
def date_part_month
|
||||
|
|
@ -34,14 +29,6 @@ class AnnualReport::TimeSeries < AnnualReport::Source
|
|||
SQL
|
||||
end
|
||||
|
||||
def annual_relationships_by_month(relationships)
|
||||
relationships
|
||||
.where(created_in_year, @year)
|
||||
.group(:period)
|
||||
.pluck(date_part_month.as('period'), Arel.star.count)
|
||||
.to_h
|
||||
end
|
||||
|
||||
def created_in_year
|
||||
Arel.sql(<<~SQL.squish)
|
||||
DATE_PART('year', created_at) = ?
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ module ApplicationExtension
|
|||
end
|
||||
|
||||
def redirect_uris
|
||||
# Doorkeeper stores the redirect_uri value as a newline delimeted list in
|
||||
# Doorkeeper stores the redirect_uri value as a newline delimited list in
|
||||
# the database:
|
||||
redirect_uri.split
|
||||
end
|
||||
|
|
|
|||
|
|
@ -455,6 +455,10 @@ class Account < ApplicationRecord
|
|||
save!
|
||||
end
|
||||
|
||||
def featureable?
|
||||
local? && discoverable?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare_contents
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class BulkImport < ApplicationRecord
|
|||
BulkImport.increment_counter(:processed_items, bulk_import_id)
|
||||
BulkImport.increment_counter(:imported_items, bulk_import_id) if imported
|
||||
|
||||
# Since the incrementation has been done atomically, concurrent access to `bulk_import` is now bening
|
||||
# Since the incrementation has been done atomically, concurrent access to `bulk_import` is now benign
|
||||
bulk_import = BulkImport.find(bulk_import_id)
|
||||
bulk_import.update!(state: :finished, finished_at: Time.now.utc) if bulk_import.processed_items == bulk_import.total_items
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
# id :bigint(8) not null, primary key
|
||||
# description :text not null
|
||||
# discoverable :boolean not null
|
||||
# item_count :integer default(0), not null
|
||||
# local :boolean not null
|
||||
# name :string not null
|
||||
# original_number_of_items :integer
|
||||
|
|
@ -39,6 +40,7 @@ class Collection < ApplicationRecord
|
|||
validate :items_do_not_exceed_limit
|
||||
|
||||
scope :with_items, -> { includes(:collection_items).merge(CollectionItem.with_accounts) }
|
||||
scope :with_tag, -> { includes(:tag) }
|
||||
|
||||
def remote?
|
||||
!local?
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
# collection_id :bigint(8) not null
|
||||
#
|
||||
class CollectionItem < ApplicationRecord
|
||||
belongs_to :collection
|
||||
belongs_to :collection, counter_cache: :item_count
|
||||
belongs_to :account, optional: true
|
||||
|
||||
enum :state,
|
||||
|
|
@ -32,6 +32,8 @@ class CollectionItem < ApplicationRecord
|
|||
validates :account, presence: true, if: :accepted?
|
||||
validates :object_uri, presence: true, if: -> { account.nil? }
|
||||
|
||||
before_validation :set_position, on: :create
|
||||
|
||||
scope :ordered, -> { order(position: :asc) }
|
||||
scope :with_accounts, -> { includes(account: [:account_stat, :user]) }
|
||||
scope :not_blocked_by, ->(account) { where.not(accounts: { id: account.blocking }) }
|
||||
|
|
@ -39,4 +41,12 @@ class CollectionItem < ApplicationRecord
|
|||
def local_item_with_remote_account?
|
||||
local? && account&.remote?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_position
|
||||
return if position_changed?
|
||||
|
||||
self.position = self.class.where(collection_id:).maximum(:position).to_i + 1
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class GeneratedAnnualReport < ApplicationRecord
|
|||
when 1
|
||||
data['most_reblogged_accounts'].pluck('account_id') + data['commonly_interacted_with_accounts'].pluck('account_id')
|
||||
when 2
|
||||
[]
|
||||
[account_id]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -64,4 +64,8 @@ class AccountPolicy < ApplicationPolicy
|
|||
def review?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def feature?
|
||||
record.featureable? && !current_account.blocking?(record) && !record.blocking?(current_account)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,9 +3,13 @@
|
|||
class REST::AnnualReportSerializer < ActiveModel::Serializer
|
||||
include RoutingHelper
|
||||
|
||||
attributes :year, :data, :schema_version, :share_url
|
||||
attributes :year, :data, :schema_version, :share_url, :account_id
|
||||
|
||||
def share_url
|
||||
public_wrapstodon_url(object.account, object.year, object.share_key) if object.share_key.present?
|
||||
end
|
||||
|
||||
def account_id
|
||||
object.account_id.to_s
|
||||
end
|
||||
end
|
||||
|
|
|
|||
12
app/serializers/rest/base_collection_serializer.rb
Normal file
12
app/serializers/rest/base_collection_serializer.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::BaseCollectionSerializer < ActiveModel::Serializer
|
||||
attributes :id, :uri, :name, :description, :local, :sensitive,
|
||||
:discoverable, :item_count, :created_at, :updated_at
|
||||
|
||||
belongs_to :tag, serializer: REST::StatusSerializer::TagSerializer
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
end
|
||||
|
|
@ -1,11 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::CollectionSerializer < ActiveModel::Serializer
|
||||
attributes :uri, :name, :description, :local, :sensitive, :discoverable,
|
||||
:created_at, :updated_at
|
||||
|
||||
class REST::CollectionSerializer < REST::BaseCollectionSerializer
|
||||
belongs_to :account, serializer: REST::AccountSerializer
|
||||
belongs_to :tag, serializer: REST::StatusSerializer::TagSerializer
|
||||
|
||||
has_many :items, serializer: REST::CollectionItemSerializer
|
||||
|
||||
|
|
|
|||
23
app/services/add_account_to_collection_service.rb
Normal file
23
app/services/add_account_to_collection_service.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddAccountToCollectionService
|
||||
def call(collection, account)
|
||||
raise ArgumentError unless collection.local?
|
||||
|
||||
@collection = collection
|
||||
@account = account
|
||||
|
||||
raise Mastodon::NotPermittedError, I18n.t('accounts.errors.cannot_be_added_to_collections') unless AccountPolicy.new(@collection.account, @account).feature?
|
||||
|
||||
create_collection_item
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_collection_item
|
||||
@collection.collection_items.create!(
|
||||
account: @account,
|
||||
state: :accepted
|
||||
)
|
||||
end
|
||||
end
|
||||
4
app/views/wrapstodon/_og_description.html.haml
Normal file
4
app/views/wrapstodon/_og_description.html.haml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
- description = t('wrapstodon.description', name: display_name(account))
|
||||
|
||||
%meta{ name: 'description', content: description }/
|
||||
= opengraph 'og:description', description
|
||||
|
|
@ -4,9 +4,15 @@
|
|||
%meta{ name: 'robots', content: 'noindex, noarchive' }/
|
||||
|
||||
= opengraph 'og:site_name', site_title
|
||||
= opengraph 'og:type', 'article'
|
||||
= opengraph 'og:title', t('wrapstodon.title', name: display_name(@account), year: @generated_annual_report.year)
|
||||
= opengraph 'profile:username', acct(@account)[1..]
|
||||
|
||||
= render 'og_description', account: @account
|
||||
|
||||
= vite_typescript_tag 'wrapstodon.tsx', crossorigin: 'anonymous'
|
||||
|
||||
- content_for :html_classes, 'theme-dark'
|
||||
|
||||
#wrapstodon
|
||||
= render_wrapstodon_share_data @generated_annual_report
|
||||
|
|
|
|||
|
|
@ -1784,16 +1784,22 @@ cs:
|
|||
body: 'Uživatel %{name} vás zmínil v:'
|
||||
subject: Uživatel %{name} vás zmínil
|
||||
title: Nová zmínka
|
||||
moderation_warning:
|
||||
subject: Obdrželi jste moderační varování
|
||||
poll:
|
||||
subject: Anketa od %{name} skončila
|
||||
quote:
|
||||
body: 'Váš příspěvek citoval účet %{name}:'
|
||||
subject: "%{name} citovali váš příspěvek"
|
||||
title: Nová citace
|
||||
quoted_update:
|
||||
subject: "%{name} upravili příspěvek, který jste citovali"
|
||||
reblog:
|
||||
body: 'Uživatel %{name} boostnul váš příspěvek:'
|
||||
subject: Uživatel %{name} boostnul váš příspěvek
|
||||
title: Nový boost
|
||||
severed_relationships:
|
||||
subject: Ztratili jste spojení, kvůli moderačnímu rozhodnutí
|
||||
status:
|
||||
subject: Nový příspěvek od %{name}
|
||||
update:
|
||||
|
|
@ -2267,3 +2273,5 @@ cs:
|
|||
not_supported: Tento prohlížeč nepodporuje bezpečnostní klíče
|
||||
otp_required: Pro použití bezpečnostních klíčů prosím nejprve zapněte dvoufázové ověřování.
|
||||
registered_on: Přidán %{date}
|
||||
wrapstodon:
|
||||
title: Wrapstodon %{year} pro %{name}
|
||||
|
|
|
|||
|
|
@ -1054,7 +1054,7 @@ de:
|
|||
one: In den vergangenen 7 Tagen von einem Profil geteilt
|
||||
other: In den vergangenen 7 Tagen von %{count} Profilen geteilt
|
||||
title: Angesagte Links
|
||||
usage_comparison: Heute %{today}-mal und gestern %{yesterday}-mal geteilt
|
||||
usage_comparison: Heute %{today} × und gestern %{yesterday} × geteilt
|
||||
not_allowed_to_trend: Darf nicht trenden
|
||||
only_allowed: Nur Genehmigte
|
||||
pending_review: Überprüfung ausstehend
|
||||
|
|
@ -1099,7 +1099,7 @@ de:
|
|||
trendable: In Trends erlaubt
|
||||
trending_rank: Platz %{rank}
|
||||
usable: In Beiträgen erlaubt
|
||||
usage_comparison: Heute %{today}-mal und gestern %{yesterday}-mal verwendet
|
||||
usage_comparison: Heute %{today} × und gestern %{yesterday} × verwendet
|
||||
used_by_over_week:
|
||||
one: In den vergangenen 7 Tagen von einem Profil verwendet
|
||||
other: In den vergangenen 7 Tagen von %{count} Profilen verwendet
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ en:
|
|||
hosted_on: Mastodon hosted on %{domain}
|
||||
title: About
|
||||
accounts:
|
||||
errors:
|
||||
cannot_be_added_to_collections: This account cannot be added to collections.
|
||||
followers:
|
||||
one: Follower
|
||||
other: Followers
|
||||
|
|
@ -2187,4 +2189,5 @@ en:
|
|||
otp_required: To use security keys please enable two-factor authentication first.
|
||||
registered_on: Registered on %{date}
|
||||
wrapstodon:
|
||||
description: See how %{name} used Mastodon this year!
|
||||
title: Wrapstodon %{year} for %{name}
|
||||
|
|
|
|||
|
|
@ -1706,16 +1706,22 @@ et:
|
|||
body: "%{name} mainis sind:"
|
||||
subject: "%{name} mainis sind"
|
||||
title: Uus mainimine
|
||||
moderation_warning:
|
||||
subject: Su kasutajakonto on saanud moderaatoritelt hoiatuse
|
||||
poll:
|
||||
subject: "%{name} küsitlus lõppes"
|
||||
quote:
|
||||
body: "%{name} tsiteeris sinu postitust:"
|
||||
subject: "%{name} tsiteeris sinu postitust"
|
||||
title: Uus tsitaat
|
||||
quoted_update:
|
||||
subject: "%{name} on muutnud sinu poolt tsiteeritud postitust"
|
||||
reblog:
|
||||
body: "%{name} jagas edasi postitust:"
|
||||
subject: "%{name} jagas postitust"
|
||||
title: Uus jagamine
|
||||
severed_relationships:
|
||||
subject: Moderaatorite otsuse tõttu oled kaotanud jälgijaid ja jälgitavaid
|
||||
status:
|
||||
subject: "%{name} postitas äsja"
|
||||
update:
|
||||
|
|
@ -2181,3 +2187,5 @@ et:
|
|||
not_supported: See veebilehitseja ei toeta turvavõtmeid
|
||||
otp_required: Turvavõtmete kasutamiseks tuleb eelnevalt sisse lülitada kaheastmeline autentimine.
|
||||
registered_on: Registreeritud %{date}
|
||||
wrapstodon:
|
||||
title: "%{year}. aasta Mastodoni kokkuvõte: %{name}"
|
||||
|
|
|
|||
|
|
@ -2185,3 +2185,5 @@ fa:
|
|||
not_supported: این مرورگر از کلیدهای امنیتی پشتیبانی نمیکند
|
||||
otp_required: برای استفاده از کلیدهای امنیتی، لطفاً ابتدا تأیید هویت دو عاملی را به کار بیندازید.
|
||||
registered_on: ثبتشده در %{date}
|
||||
wrapstodon:
|
||||
title: خلاصهٔ ماستودون %{year} برای %{name}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ fi:
|
|||
inbox_url: Postilaatikon URL-osoite
|
||||
invite_request_text: Syitä liittymiseen
|
||||
invited_by: Kutsuja
|
||||
ip: IP-osoite
|
||||
ip: IP-osoite
|
||||
joined: Liittynyt
|
||||
location:
|
||||
all: Kaikki
|
||||
|
|
@ -569,8 +569,8 @@ fi:
|
|||
all: Kaikki
|
||||
clear: Tyhjennä toimitusvirheet
|
||||
failing: Epäonnistuva
|
||||
restart: Käynnistä toimitus uudelleen
|
||||
stop: Lopeta toimitus
|
||||
restart: Jatka toimitusta
|
||||
stop: Pysäytä toimitus
|
||||
unavailable: Ei saatavilla
|
||||
delivery_available: Toimitus on saatavilla
|
||||
delivery_error_days: Toimitusvirheen päivät
|
||||
|
|
@ -616,14 +616,14 @@ fi:
|
|||
created_msg: Uusi IP-sääntö lisättiin onnistuneesti
|
||||
delete: Poista
|
||||
expires_in:
|
||||
'1209600': 2 viikkoa
|
||||
'15778476': 6 kuukautta
|
||||
'2629746': 1 kuukausi
|
||||
'31556952': 1 vuosi
|
||||
'86400': 1 päivä
|
||||
'94670856': 3 vuotta
|
||||
'1209600': 2 viikkoa
|
||||
'15778476': 6 kuukautta
|
||||
'2629746': 1 kuukausi
|
||||
'31556952': 1 vuosi
|
||||
'86400': 1 päivä
|
||||
'94670856': 3 vuotta
|
||||
new:
|
||||
title: Luo uusi IP-sääntö
|
||||
title: Luo uusi IP-sääntö
|
||||
no_ip_block_selected: IP-sääntöjä ei muutettu, koska yhtään ei ollut valittuna
|
||||
title: IP-säännöt
|
||||
relationships:
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ lt:
|
|||
demote: Pažeminti
|
||||
destroyed_msg: "%{username} duomenys dabar laukia eilėje, kad būtų netrukus ištrinti"
|
||||
disable: Sustabdyti
|
||||
disable_sign_in_token_auth: Išjungti el. pašto prieigos rakto tapatybės nustatymą
|
||||
disable_two_factor_authentication: Išjungti 2FA
|
||||
disabled: Pristabdyta
|
||||
display_name: Rodomas vardas
|
||||
|
|
@ -73,6 +74,7 @@ lt:
|
|||
email: El. paštas
|
||||
email_status: El. pašto būsena
|
||||
enable: Panaikinti sustabdymą
|
||||
enable_sign_in_token_auth: Įjungti el. pašto prieigos rakto tapatybės nustatymą
|
||||
enabled: Įjungta
|
||||
enabled_msg: Sėkmingai %{username} paskyros sustabdymas panaikintas
|
||||
followers: Sekėjai
|
||||
|
|
@ -139,13 +141,14 @@ lt:
|
|||
resubscribe: Prenumeruoti iš naujo
|
||||
role: Vaidmuo
|
||||
search: Ieškoti
|
||||
search_same_email_domain: Kiti naudotojai su tuo pačiu el. pašto domenu
|
||||
search_same_ip: Kiti naudotojai su tuo pačiu IP
|
||||
security: Saugumas
|
||||
security_measures:
|
||||
only_password: Tik slaptažodis
|
||||
password_and_2fa: Slaptažodis ir 2FA
|
||||
sensitive: Priversti žymėti kaip jautrią
|
||||
sensitized: Pažymėta kaip jautri
|
||||
sensitive: Priversti žymėti kaip su jautria informacija
|
||||
sensitized: Pažymėti kaip jautrią informaciją
|
||||
shared_inbox_url: Bendras gautiejų URL
|
||||
show:
|
||||
created_reports: Sukurtos ataskaitos
|
||||
|
|
@ -163,7 +166,7 @@ lt:
|
|||
unblock_email: Atblokuoti el. pašto adresą
|
||||
unblocked_email_msg: Sėkmingai atblokuotas %{username} el. pašto adresas.
|
||||
unconfirmed_email: Nepatvirtintas el. pašto adresas.
|
||||
undo_sensitized: Atšaukti privertimą žymėti kaip jautrią
|
||||
undo_sensitized: Atšaukti privertimą žymėti kaip jautrią informaciją
|
||||
undo_silenced: Atšaukti ribojimą
|
||||
undo_suspension: Atšaukti pristabdymą
|
||||
unsilenced_msg: Sėkmingai atšauktas %{username} paskyros ribojimas.
|
||||
|
|
@ -179,13 +182,16 @@ lt:
|
|||
approve_appeal: Patvirtinti apeliaciją
|
||||
approve_user: Patvirtinti naudotoją
|
||||
assigned_to_self_report: Priskirti ataskaitą
|
||||
change_email_user: Keisti el. paštą vartotjui
|
||||
change_role_user: Keisti naudotojo vaidmenį
|
||||
confirm_user: Patvirtinti naudotoją
|
||||
create_account_warning: Kurti įspėjimą
|
||||
create_announcement: Kurti skelbimą
|
||||
create_canonical_email_block: Sukurti el. pašto blokavimą
|
||||
create_custom_emoji: Kurti pasirinktinį jaustuką
|
||||
create_domain_allow: Kurti domeno leidimą
|
||||
create_domain_block: Kurti domeno bloką
|
||||
create_email_domain_block: Sukurti el. pašto domeno blokavimą
|
||||
create_ip_block: Kurti IP taisyklę
|
||||
create_relay: Kurti perdavimą
|
||||
create_unavailable_domain: Kurti nepasiekiamą domeną
|
||||
|
|
@ -193,9 +199,11 @@ lt:
|
|||
create_username_block: Kurti naudotojo vardo taisyklę
|
||||
demote_user: Pažeminti naudotoją
|
||||
destroy_announcement: Ištrinti skelbimą
|
||||
destroy_canonical_email_block: Ištrinti el. pašto blokavimą
|
||||
destroy_custom_emoji: Ištrinti pasirinktinį jaustuką
|
||||
destroy_domain_allow: Ištrinti domeno leidimą
|
||||
destroy_domain_block: Ištrinti domeno bloką
|
||||
destroy_email_domain_block: Ištrinti el. pašto domeno blokavimą
|
||||
destroy_instance: Išvalyti domeną
|
||||
destroy_ip_block: Ištrinti IP taisyklę
|
||||
destroy_relay: Ištrinti perdavimą
|
||||
|
|
@ -206,6 +214,7 @@ lt:
|
|||
disable_2fa_user: Išjungti 2FA
|
||||
disable_custom_emoji: Išjungti pasirinktinį jaustuką
|
||||
disable_relay: Išjungti perdavimą
|
||||
disable_sign_in_token_auth_user: Išjungti el. pašto prieigos rakto tapatybės nustatymą naudotojui
|
||||
disable_user: Išjungti naudotoją
|
||||
enable_custom_emoji: Įjungti pasirinktinį jaustuką
|
||||
enable_relay: Įjungti perdavimą
|
||||
|
|
@ -220,12 +229,12 @@ lt:
|
|||
resend_user: Iš naujo siųsti patvirtinimo laišką
|
||||
reset_password_user: Nustatyti iš naujo slaptažodį
|
||||
resolve_report: Išspręsti ataskaitą
|
||||
sensitive_account: Priversti žymėti kaip jautrią paskyrai
|
||||
sensitive_account: Nustatyti kaip jautrios informacijos paskyrą
|
||||
silence_account: Riboti paskyrą
|
||||
suspend_account: Pristabdyti paskyrą
|
||||
unassigned_report: Panaikinti priskyrimą ataskaitą
|
||||
unblock_email_account: Atblokuoti el. pašto adresą
|
||||
unsensitive_account: Atšaukti privertimą žymėti kaip jautrią paskyrai
|
||||
unsensitive_account: Atšaukti privertimą žymėti kaip jautrios informacijos paskyrą
|
||||
unsilence_account: Atšaukti riboti paskyrą
|
||||
unsuspend_account: Atšaukti paskyros pristabdymą
|
||||
update_announcement: Atnaujinti skelbimą
|
||||
|
|
@ -459,9 +468,19 @@ lt:
|
|||
new:
|
||||
title: Importuoti domeno blokavimus
|
||||
no_file: Nėra pasirinkto failo
|
||||
fasp:
|
||||
providers:
|
||||
sign_in: Prisijungti
|
||||
title: Fediverse Auxiliary Service tiekėjai
|
||||
title: FAST
|
||||
follow_recommendations:
|
||||
description_html: "<strong>Rekomendacijos padeda naujiems vartotojams greitai rasti įdomų turinį</strong>. Kai vartotojas nėra pakankamai bendravęs su kitais, kad būtų galima suformuoti asmeninį turinį, vietoj to rekomenduojami šios paskyros. Jos perskaičiuojamos kasdien, remiantis kas pastaruoju metu sulaukė didžiausio susidomėjimo ir turi daugiausiai vietinių sekėjų tam tikra kalba."
|
||||
language: Kalbui
|
||||
status: Būsena
|
||||
suppress: Slėpti rekomendacijas
|
||||
suppressed: Paslėpta
|
||||
title: Sekti rekomendacijas
|
||||
unsuppress: Atkurti rekomendaciją
|
||||
instances:
|
||||
audit_log:
|
||||
title: Naujausi audito žurnalai
|
||||
|
|
@ -502,6 +521,8 @@ lt:
|
|||
expired: Nebegaliojantis
|
||||
title: Filtras
|
||||
title: Kvietimai
|
||||
ip_blocks:
|
||||
delete: Ištrinti
|
||||
relays:
|
||||
add_new: Pridėti naują pamainą
|
||||
delete: Ištrinti
|
||||
|
|
@ -564,7 +585,7 @@ lt:
|
|||
invite_users_description: Leidžia naudotojams pakviesti naujus žmones į serverį.
|
||||
manage_invites: Tvarkyti kvietimus
|
||||
manage_invites_description: Leidžia naudotojams naršyti ir deaktyvuoti kvietimų nuorodas.
|
||||
manage_taxonomies_description: Leidžia naudotojams peržiūrėti tendencingą turinį ir atnaujinti saitažodžių nustatymus
|
||||
manage_taxonomies_description: Leidžia naudotojams peržiūrėti tendencingą turinį ir atnaujinti grotažymių nustatymus
|
||||
settings:
|
||||
branding:
|
||||
title: Firminio ženklo kūrimas
|
||||
|
|
@ -661,7 +682,8 @@ lt:
|
|||
open: Peržiūrėti viešai
|
||||
reset: Atkurti
|
||||
search: Paieška
|
||||
title: Saitažodžiai
|
||||
title: Grotažymė
|
||||
updated_msg: Grotažymės nustatymai sėkmingai atnaujinti
|
||||
terms_of_service:
|
||||
back: Atgal į paslaugų sąlygas
|
||||
changelog: Kas pasikeitė
|
||||
|
|
@ -749,9 +771,10 @@ lt:
|
|||
tag_servers_dimension: Populiariausi serveriai
|
||||
tag_servers_measure: skirtingi serveriai
|
||||
tag_uses_measure: bendri naudojimai
|
||||
description_html: Tai yra grotažymės, kurios šiuo metu dažnai pasirodo daugelyje jūsų serverio matomų įrašų. Tai gali padėti jūsų vartotojams sužinoti, apie ką žmonės šiuo metu kalba daugiausiai. Grotažymės nėra rodomos viešai, kol jūs jų nepatvirtinate.
|
||||
listable: Gali būti siūloma
|
||||
not_trendable: Nepasirodys tendencijose
|
||||
title: Tendencingos saitažodžiai
|
||||
title: Populiarios grotažymės
|
||||
trendable: Gali pasirodyti tendencijose
|
||||
trending_rank: 'Tendencinga #%{rank}'
|
||||
title: Rekomendacijos ir tendencijos
|
||||
|
|
@ -806,10 +829,12 @@ lt:
|
|||
new_trending_statuses:
|
||||
title: Tendencingi įrašai
|
||||
new_trending_tags:
|
||||
title: Tendencingos saitažodžiai
|
||||
title: Populiarios grotažymės
|
||||
subject: Naujos tendencijos peržiūrimos %{instance}
|
||||
appearance:
|
||||
animations_and_accessibility: Animacijos ir pritaikymas neįgaliesiems
|
||||
boosting_preferences: Pasidalinimo nustatymai
|
||||
boosting_preferences_info_html: "<strong>Patarimas:</strong> Nepriklausomai nuo nustatymų, <kbd>Shift</kbd> + <kbd>Click</kbd> ant %{icon} Dalintis ikonos iš karto pasidalins įrašu."
|
||||
discovery: Atradimas
|
||||
localization:
|
||||
body: Mastodon verčia savanoriai.
|
||||
|
|
@ -971,7 +996,7 @@ lt:
|
|||
storage: Medijos saugykla
|
||||
featured_tags:
|
||||
add_new: Pridėti naują
|
||||
hint_html: "<strong>Savo profilyje parodyk svarbiausius saitažodžius.</strong> Tai puikus įrankis kūrybiniams darbams ir ilgalaikiams projektams sekti, todėl svarbiausios saitažodžiai rodomi matomoje vietoje profilyje ir leidžia greitai pasiekti tavo paties įrašus."
|
||||
hint_html: "<strong>Savo profilyje parodyk svarbiausias grotažymes.</strong> Tai puikus įrankis kūrybiniams darbams ir ilgalaikiams projektams sekti, todėl svarbiausios grotažymės rodomos matomoje vietoje profilyje ir leidžia greitai pasiekti tavo paties įrašus."
|
||||
filters:
|
||||
contexts:
|
||||
account: Profiliai
|
||||
|
|
@ -996,7 +1021,7 @@ lt:
|
|||
changes_saved_msg: Pakeitimai sėkmingai išsaugoti!
|
||||
copy: Kopijuoti
|
||||
delete: Ištrinti
|
||||
deselect: Panaikinti visus žymėjimus
|
||||
deselect: Atžymėti visus
|
||||
order_by: Tvarkyti pagal
|
||||
save_changes: Išsaugoti pakeitimus
|
||||
today: šiandien
|
||||
|
|
@ -1050,6 +1075,9 @@ lt:
|
|||
title: Tapatybės nustatymo istorija
|
||||
mail_subscriptions:
|
||||
unsubscribe:
|
||||
emails:
|
||||
notification_emails:
|
||||
reblog: dalintis pranešimų el. pašto laiškais
|
||||
success_html: Daugiau negausi %{type} „Mastodon“ domene %{domain} į savo el. paštą %{email}.
|
||||
media_attachments:
|
||||
validations:
|
||||
|
|
@ -1087,9 +1115,9 @@ lt:
|
|||
subject: "%{name} paminėjo jūsų įrašą"
|
||||
title: Naujas paminėjimas
|
||||
reblog:
|
||||
body: 'Tavo įrašą pakėlė %{name}:'
|
||||
subject: "%{name} pakėlė tavo įrašą"
|
||||
title: Naujas pakėlimas
|
||||
body: 'Tavo įrašą dalinosi %{name}:'
|
||||
subject: "%{name} dalinosi tavo įrašu"
|
||||
title: Naujas dalinimasis
|
||||
notifications:
|
||||
administration_emails: Administratoriaus el. laiško pranešimai
|
||||
email_events: Įvykiai, skirti el. laiško pranešimams
|
||||
|
|
@ -1144,6 +1172,9 @@ lt:
|
|||
status: Paskyros būsena
|
||||
remote_follow:
|
||||
missing_resource: Jūsų paskyros nukreipimo URL nerasta
|
||||
rss:
|
||||
descriptions:
|
||||
tag: 'Vieši įrašai su grotažymėmis #%{hashtag}'
|
||||
scheduled_statuses:
|
||||
over_daily_limit: Jūs pasieketė limitą (%{limit}) galimų toot'ų per dieną
|
||||
over_total_limit: Jūs pasieketė %{limit} limitą galimų toot'ų
|
||||
|
|
@ -1187,7 +1218,7 @@ lt:
|
|||
development: Kūrimas
|
||||
edit_profile: Redaguoti profilį
|
||||
export: Eksportuoti
|
||||
featured_tags: Rodomi saitažodžiai
|
||||
featured_tags: Išskirtos grotažymės
|
||||
import: Importuoti
|
||||
import_and_export: Importas ir eksportas
|
||||
migrate: Paskyros migracija
|
||||
|
|
@ -1215,7 +1246,7 @@ lt:
|
|||
many: "%{count} vaizdo"
|
||||
one: "%{count} vaizdas"
|
||||
other: "%{count} vaizdų"
|
||||
boosted_from_html: Pakelta iš %{acct_link}
|
||||
boosted_from_html: Dalintasi iš %{acct_link}
|
||||
content_warning: 'Turinio įspėjimas: %{warning}'
|
||||
errors:
|
||||
quoted_status_not_found: Įrašas, kurį bandote cituoti, atrodo, neegzistuoja.
|
||||
|
|
@ -1224,7 +1255,7 @@ lt:
|
|||
pin_errors:
|
||||
limit: Jūs jau prisegėte maksimalų toot'ų skaičų
|
||||
ownership: Kitų vartotojų toot'ai negali būti prisegti
|
||||
reblog: Pakeltos žinutės negali būti prisegtos
|
||||
reblog: Žinutės, kuriomis pasidalinta, negali būti papildomai prisegtos
|
||||
quote_error:
|
||||
not_available: Įrašas nepasiekiamas
|
||||
pending_approval: Įrašas peržiūrimas
|
||||
|
|
@ -1239,20 +1270,23 @@ lt:
|
|||
public: Vieša
|
||||
statuses_cleanup:
|
||||
enabled_hint: Automatiškai ištrina įrašus, kai jie pasiekia nustatytą amžiaus ribą, nebent jie atitinka vieną iš toliau nurodytų išimčių
|
||||
ignore_reblogs: Ignoruoti pasidalinimus
|
||||
interaction_exceptions_explanation: Atkreipk dėmesį, kad negarantuojama, jog įrašai nebus ištrinti, jei jų mėgstamumo ar pasidalinimo riba bus žemesnė, nors vieną kartą ji jau buvo viršyta.
|
||||
keep_polls_hint: Neištrina jokių tavo apklausų
|
||||
keep_self_bookmark: Laikyti įrašus, kuriuos pažymėjai
|
||||
keep_self_bookmark: Laikyti įrašus, kuriuos pažymėjai su žyma
|
||||
keep_self_bookmark_hint: Neištrina tavo pačių įrašų, jei esi juos pažymėjęs (-usi)
|
||||
keep_self_fav_hint: Neištrina tavo pačių įrašų, jei esi juos pamėgęs (-usi)
|
||||
min_age_label: Amžiaus riba
|
||||
min_reblogs: Pasidalintus įrašus laikyti bent
|
||||
min_reblogs_hint: Neištrina jokių jūsų įrašų, kuriais buvo dalintasi bent tiek kartų. Palikite tuščią laukelį, jei norite ištrinti pasidalitus įrašus, nepriklausomai nuo jų paskelbimų skaičiaus
|
||||
stream_entries:
|
||||
sensitive_content: Jautrus turinys
|
||||
terms_of_service:
|
||||
title: Paslaugų sąlygos
|
||||
themes:
|
||||
contrast: Mastodon (didelis kontrastas)
|
||||
default: Mastodon (tamsi)
|
||||
mastodon-light: Mastodon (šviesi)
|
||||
default: Mastodon (Tamsus)
|
||||
mastodon-light: Mastodon (Šviesus)
|
||||
system: Automatinis (naudoti sistemos temą)
|
||||
two_factor_authentication:
|
||||
add: Pridėti
|
||||
|
|
@ -1337,8 +1371,8 @@ lt:
|
|||
follows_title: Ką sekti
|
||||
follows_view_more: Peržiūrėti daugiau sekamų žmonių
|
||||
hashtags_subtitle: Naršyk, kas tendencinga per pastarąsias 2 dienas
|
||||
hashtags_title: Tendencingos saitažodžiai
|
||||
hashtags_view_more: Peržiūrėti daugiau tendencingų saitažodžių
|
||||
hashtags_title: Populiarios grotažymės
|
||||
hashtags_view_more: Peržiūrėti daugiau populiarių grotažymių
|
||||
post_action: Sukurti
|
||||
post_step: Sakyk labas pasauliui tekstu, nuotraukomis, vaizdo įrašais arba apklausomis.
|
||||
post_title: Sukūrk savo pirmąjį įrašą
|
||||
|
|
|
|||
|
|
@ -1424,6 +1424,70 @@ nan:
|
|||
statuses: 個別ê PO文
|
||||
statuses_hint_html: Tsit ê過濾器應用佇所揀ê個別ê PO文,毋管in敢有符合下kha ê關鍵字<a href="%{path}">重頭看,á是kā PO文tuì tsit ê過濾器suá掉</a>。
|
||||
title: 編輯過濾器
|
||||
errors:
|
||||
deprecated_api_multiple_keywords: Tsiah ê參數bē當tuì tsit ê應用程式改,因為in應用超過tsi̍t个過濾關鍵字。請用khah新ê應用程式,á是網頁界面。
|
||||
invalid_context: 無提供內文á是內文無效
|
||||
index:
|
||||
contexts: "%{contexts} 內底ê過濾器"
|
||||
delete: Thâi掉
|
||||
empty: Lí bô半个過濾器。
|
||||
expires_in: 佇 %{distance} kàu期
|
||||
expires_on: 佇 %{date} kàu期
|
||||
keywords:
|
||||
other: "%{count} ê關鍵字"
|
||||
statuses:
|
||||
other: "%{count} 篇PO文"
|
||||
statuses_long:
|
||||
other: "%{count} 篇khàm掉ê個別PO文"
|
||||
title: 過濾器
|
||||
new:
|
||||
save: 儲存新ê過濾器
|
||||
title: 加新ê過濾器
|
||||
statuses:
|
||||
back_to_filter: 轉去過濾器
|
||||
batch:
|
||||
remove: Tuì過濾器內suá掉
|
||||
index:
|
||||
hint: Tsit ê過濾器應用kàu所揀ê個別PO文,無論敢有其他ê條件。Lí ē當tuì網頁界面加koh khah tsē PO文kàu tsit ê過濾器。
|
||||
title: 過濾ê PO文
|
||||
generic:
|
||||
all: 全部
|
||||
all_items_on_page_selected_html:
|
||||
other: Tsit頁ê %{count} ê項目有揀ah。
|
||||
all_matching_items_selected_html:
|
||||
other: 符合lí ê tshiau-tshuē ê %{count} ê項目有揀ah。
|
||||
cancel: 取消
|
||||
changes_saved_msg: 改變儲存成功!
|
||||
confirm: 確認
|
||||
copy: Khóo-pih
|
||||
delete: Thâi掉
|
||||
deselect: 取消lóng揀
|
||||
none: 無
|
||||
order_by: 排列照
|
||||
save_changes: 儲存改變
|
||||
select_all_matching_items:
|
||||
other: 揀 %{count} ê符合lí所tshiau-tshuē ê項目。
|
||||
today: 今á日
|
||||
validation_errors:
|
||||
other: 干焦iáu有狀況!請看下kha ê %{count} 項錯誤。
|
||||
imports:
|
||||
errors:
|
||||
empty: 空ê CSV檔案
|
||||
incompatible_type: Kap所揀ê輸入類型無配合
|
||||
invalid_csv_file: 無效ê CSV檔案。錯誤:%{error}
|
||||
over_rows_processing_limit: 包含超過 %{count} tsuā
|
||||
too_large: 檔案siunn大
|
||||
failures: 失敗
|
||||
imported: 輸入ah
|
||||
mismatched_types_warning: Lí ká-ná kā輸入類型揀毋著,請koh確認。
|
||||
modes:
|
||||
merge: 合併
|
||||
merge_long: 保存已經有ê記錄,koh加新ê
|
||||
overwrite: Khàm掉
|
||||
overwrite_long: 用新ê記錄khàm掉tann ê
|
||||
overwrite_preambles:
|
||||
blocking_html:
|
||||
other: Lí teh-beh用 <strong>%{filename}</strong> ê <strong>%{count} ê口座</strong>,<strong>替換lí ê封鎖列單</strong>。
|
||||
scheduled_statuses:
|
||||
too_soon: Tio̍h用未來ê日期。
|
||||
statuses:
|
||||
|
|
|
|||
|
|
@ -2185,3 +2185,5 @@ nl:
|
|||
not_supported: Deze browser ondersteunt geen beveiligingssleutels
|
||||
otp_required: Om beveiligingssleutels te kunnen gebruiken, moet je eerst tweestapsverificatie inschakelen.
|
||||
registered_on: Geregistreerd op %{date}
|
||||
wrapstodon:
|
||||
title: Wrapstodon %{year} voor %{name}
|
||||
|
|
|
|||
|
|
@ -40,14 +40,14 @@ de:
|
|||
text: Du kannst nur einmal Einspruch gegen eine Maßnahme einlegen
|
||||
defaults:
|
||||
autofollow: Personen, die sich über deine Einladung registrieren, folgen automatisch deinem Profil
|
||||
avatar: WEBP, PNG, GIF oder JPG. Höchstens %{size} groß. Wird auf %{dimensions} px verkleinert
|
||||
avatar: WebP, PNG, GIF oder JPG. Höchstens %{size} groß. Wird auf %{dimensions} px verkleinert
|
||||
bot: Signalisiert, dass dieses Konto hauptsächlich automatisierte Aktionen durchführt und möglicherweise nicht persönlich betreut wird
|
||||
context: Orte, an denen der Filter aktiv sein soll
|
||||
current_password: Gib aus Sicherheitsgründen bitte das Passwort des aktuellen Kontos ein
|
||||
current_username: Um das zu bestätigen, gib den Profilnamen des aktuellen Kontos ein
|
||||
digest: Wenn du eine längere Zeit inaktiv bist oder du während deiner Abwesenheit in einer privaten Nachricht erwähnt worden bist
|
||||
email: Du wirst eine E-Mail zur Verifizierung dieser E-Mail-Adresse erhalten
|
||||
header: WEBP, PNG, GIF oder JPG. Höchstens %{size} groß. Wird auf %{dimensions} px verkleinert
|
||||
header: WebP, PNG, GIF oder JPG. Höchstens %{size} groß. Wird auf %{dimensions} px verkleinert
|
||||
inbox_url: Kopiere die URL von der Startseite des gewünschten Relais
|
||||
irreversible: Bereinigte Beiträge verschwinden unwiderruflich für dich, auch dann, wenn dieser Filter zu einem späteren wieder entfernt wird
|
||||
locale: Die Sprache der Bedienoberfläche, E-Mails und Push-Benachrichtigungen
|
||||
|
|
@ -86,13 +86,13 @@ de:
|
|||
warn: Den gefilterten Beitrag hinter einer Warnung, die den Filtertitel beinhaltet, ausblenden
|
||||
form_admin_settings:
|
||||
activity_api_enabled: Anzahl der wöchentlichen Beiträge, aktiven Profile und Registrierungen auf diesem Server
|
||||
app_icon: WEBP, PNG, GIF oder JPG. Überschreibt das Standard-App-Symbol auf mobilen Geräten mit einem eigenen Symbol.
|
||||
app_icon: WebP, PNG, GIF oder JPG. Überschreibt das Standard-App-Symbol auf mobilen Geräten mit einem eigenen Symbol.
|
||||
backups_retention_period: Nutzer*innen haben die Möglichkeit, Archive ihrer Beiträge zu erstellen, die sie später herunterladen können. Wenn ein positiver Wert gesetzt ist, werden diese Archive nach der festgelegten Anzahl von Tagen automatisch aus deinem Speicher gelöscht.
|
||||
bootstrap_timeline_accounts: Diese Konten werden an den Anfang der Follow-Empfehlungen für neue Nutzer angeheftet. Gib eine durch Kommata getrennte Liste von Konten an.
|
||||
closed_registrations_message: Wird angezeigt, wenn Registrierungen deaktiviert sind
|
||||
content_cache_retention_period: Sämtliche Beiträge von anderen Servern (einschließlich geteilte Beiträge und Antworten) werden, unabhängig von der Interaktion der lokalen Nutzer*innen mit diesen Beiträgen, nach der festgelegten Anzahl von Tagen gelöscht. Das betrifft auch Beiträge, die von lokalen Nutzer*innen favorisiert oder als Lesezeichen gespeichert wurden. Private Erwähnungen zwischen Nutzer*innen von verschiedenen Servern werden ebenfalls verloren gehen und können nicht wiederhergestellt werden. Diese Option richtet sich ausschließlich an Server mit speziellen Zwecken und wird die allgemeine Nutzungserfahrung beeinträchtigen, wenn sie für den allgemeinen Gebrauch aktiviert ist.
|
||||
custom_css: Du kannst eigene Stylesheets für das Webinterface von Mastodon verwenden.
|
||||
favicon: WEBP, PNG, GIF oder JPG. Überschreibt das Standard-Mastodon-Favicon mit einem eigenen Symbol.
|
||||
favicon: WebP, PNG, GIF oder JPG. Überschreibt das Standard-Mastodon-Favicon mit einem eigenen Favicon.
|
||||
landing_page: Legt fest, welche Seite neue Besucher*innen sehen, wenn sie zum ersten Mal auf deinem Server ankommen. Für „Trends“ müssen die Trends in den Entdecken-Einstellungen aktiviert sein. Für „Lokaler Feed“ muss „Zugriff auf Live-Feeds, die lokale Beiträge beinhalten“ in den Entdecken-Einstellungen auf „Alle“ gesetzt werden.
|
||||
mascot: Überschreibt die Abbildung im erweiterten Webinterface.
|
||||
media_cache_retention_period: Mediendateien aus Beiträgen von externen Nutzer*innen werden auf deinem Server zwischengespeichert. Wenn ein positiver Wert gesetzt ist, werden die Medien nach der festgelegten Anzahl von Tagen gelöscht. Sollten die Medien nach dem Löschvorgang wieder angefragt werden, werden sie erneut heruntergeladen, sofern der ursprüngliche Inhalt noch vorhanden ist. Es wird empfohlen, diesen Wert auf mindestens 14 Tage festzulegen, da die Häufigkeit der Abfrage von Linkvorschaukarten für Websites von Dritten begrenzt ist und die Linkvorschaukarten sonst nicht vor Ablauf dieser Zeit aktualisiert werden.
|
||||
|
|
@ -229,7 +229,7 @@ de:
|
|||
locale: Sprache des Webinterface
|
||||
max_uses: Maximale Anzahl von Verwendungen
|
||||
new_password: Neues Passwort
|
||||
note: Biografie
|
||||
note: Über mich
|
||||
otp_attempt: Zwei-Faktor-Authentisierung
|
||||
password: Passwort
|
||||
phrase: Wort oder Formulierung
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ fa:
|
|||
setting_display_media_hide_all: همیشه همهٔ عکسها و ویدیوها را پنهان کن
|
||||
setting_display_media_show_all: همیشه تصویرهایی را که به عنوان حساس علامت زده شدهاند را نشان بده
|
||||
setting_emoji_style: چگونگی نمایش شکلکها. «خودکار» تلاش خواهد کرد از شکلکهای بومی استفاده کند؛ ولی برای مرورگرهای قدیمی به توییموجی برخواهد گشت.
|
||||
setting_quick_boosting_html: هنگام به کار افتادن زدن روی %{boost_icon} نقشک تقویت به جای گشودن فهرست پایین افتادنی تقویت و نقل، بلافاصله تقویت خواهد کرد. کنش نقل قول را به فهرست %{options_icon} (گزینهها) منتقل میکند.
|
||||
setting_quick_boosting_html: هنگام به کار افتادن، زدن روی %{boost_icon} نقشک تقویت به جای گشودنِ فهرست پایین افتادنی تقویت و نقل، بلافاصله تقویت خواهد کرد. کنشِ نقل قول را به فهرست %{options_icon} (گزینهها) منتقل میکند.
|
||||
setting_system_scrollbars_ui: فقط برای مرورگرهای دسکتاپ مبتنی بر سافاری و کروم اعمال می شود
|
||||
setting_use_blurhash: سایهها بر اساس رنگهای بهکاررفته در تصویر پنهانشده ساخته میشوند ولی جزئیات تصویر در آنها آشکار نیست
|
||||
setting_use_pending_items: به جای پیشرفتن خودکار در فهرست، بهروزرسانی فهرست نوشتهها را پشت یک کلیک پنهان کن
|
||||
|
|
@ -237,7 +237,7 @@ fa:
|
|||
setting_aggregate_reblogs: تقویتها را در خطزمانی گروهبندی کن
|
||||
setting_always_send_emails: فرستادن همیشگی آگاهیهای رایانامهای
|
||||
setting_auto_play_gif: پخش خودکار تصویرهای متحرک
|
||||
setting_boost_modal: واپایش نمایانی توثیت
|
||||
setting_boost_modal: واپایش نمایانی تقویت
|
||||
setting_default_language: زبان نوشتههای شما
|
||||
setting_default_privacy: نمایانی فرسته
|
||||
setting_default_quote_policy: افراد مجاز به نقل
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ lt:
|
|||
display_name: Tavo pilnas vardas arba smagus vardas.
|
||||
fields: Tavo pagrindinis puslapis, įvardžiai, amžius, bet kas, ko tik nori.
|
||||
indexable: Tavo vieši įrašai gali būti rodomi Mastodon paieškos rezultatuose. Žmonės, kurie bendravo su tavo įrašais, gali jų ieškoti nepriklausomai nuo to.
|
||||
note: 'Gali @paminėti kitus žmones arba #saitažodžius.'
|
||||
note: 'Gali @paminėti kitus žmones arba #grotažymes.'
|
||||
show_collections: Žmonės galės peržiūrėti tavo sekimus ir sekėjus. Žmonės, kuriuos seki, matys, kad juos seki, nepaisant to.
|
||||
unlocked: Žmonės galės jus sekti nepaprašę patvirtinimo. Panaikinkite žymėjimą, jei norite peržiūrėti sekimo prašymus ir pasirinkti, ar priimti, ar atmesti naujus sekėjus.
|
||||
account_alias:
|
||||
|
|
@ -55,8 +55,9 @@ lt:
|
|||
phrase: Bus suderinta, neatsižvelgiant į teksto lygį arba įrašo turinio įspėjimą
|
||||
scopes: Prie kurių API programai bus leidžiama pasiekti. Pasirinkus aukščiausio lygio sritį, atskirų sričių pasirinkti nereikia.
|
||||
setting_advanced_layout: Rodykite „Mastodon“ kaip kelių stulpelių išdėstymą, leidžiantį peržiūrėti įrašų srautą, pranešimus ir trečiąjį stulpelį pagal savo pasirinkimą. Nerekomenduojama mažesniems ekranams.
|
||||
setting_aggregate_reblogs: Nerodyti naujų pakėlimų įrašams, kurie neseniai buvo pakelti (taikoma tik naujai gautiems pakėlimams).
|
||||
setting_aggregate_reblogs: Neberodyti naujų pasidalinimų įrašais, kuriais neseniai buvo pasidalinta (taikoma tik naujai daromiems pasidalinimams)
|
||||
setting_always_send_emails: Paprastai el. laiško pranešimai nebus siunčiami, kai aktyviai naudoji Mastodon.
|
||||
setting_boost_modal: Kai ši funkcija įjungta, pasidalijant įrašu pirmiausia atsivers patvirtinimo langas, kuriame galėsite pakeisti savo pasidalijimo matomumo nustatymus.
|
||||
setting_default_quote_policy_private: Tik sekėjams skirti įrašai, paskelbti platformoje „Mastodon“, negali būti cituojami kitų.
|
||||
setting_default_quote_policy_unlisted: Kai žmonės jus cituos, jų įrašai taip pat bus paslėpti iš populiariausių naujienų srauto.
|
||||
setting_default_sensitive: Jautrioji medija pagal numatytuosius nustatymus yra paslėpta ir gali būti atskleista spustelėjus.
|
||||
|
|
@ -70,10 +71,12 @@ lt:
|
|||
setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio srauto slinkimo.
|
||||
username: Gali naudoti raides, skaičius ir pabraukimus
|
||||
whole_word: Kai raktažodis ar frazė yra tik raidinis ir skaitmeninis, jis bus taikomas tik tada, jei atitiks visą žodį
|
||||
domain_allow:
|
||||
domain: Nurodytas domenas galės gauti duomenis iš šio serverio ir iš jo gaunami duomenys bus apdorojami ir saugomi
|
||||
email_domain_block:
|
||||
with_dns_records: Bus bandoma išspręsti nurodyto domeno DNS įrašus, o rezultatai taip pat bus blokuojami
|
||||
featured_tag:
|
||||
name: 'Štai keletas pastaruoju metu dažniausiai saitažodžių, kurių tu naudojai:'
|
||||
name: 'Štai keletas pastaruoju metu dažniausiai tavo naudotų grotažymių:'
|
||||
filters:
|
||||
actions:
|
||||
blur: Slėpti mediją po įspėjimu, neslepiant paties teksto
|
||||
|
|
@ -94,8 +97,9 @@ lt:
|
|||
site_contact_email: Kaip žmonės gali su tavimi susisiekti teisiniais ar pagalbos užklausimais.
|
||||
site_contact_username: Kaip žmonės gali tave pasiekti Mastodon.
|
||||
site_extended_description: Bet kokia papildoma informacija, kuri gali būti naudinga lankytojams ir naudotojams. Gali būti struktūrizuota naudojant Markdown sintaksę.
|
||||
theme: Tema, kurią mato atsijungę lankytojai ir nauji vartotojai.
|
||||
thumbnail: Maždaug 2:1 dydžio vaizdas, rodomas šalia tavo serverio informacijos.
|
||||
trends: Trendai rodo, kurios įrašai, saitažodžiai ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo.
|
||||
trends: Tendencijos rodo, kurie įrašai, grotažymės ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo.
|
||||
imports:
|
||||
data: CSV failas, eksportuotas iš kito „Mastodon“ serverio.
|
||||
invite_request:
|
||||
|
|
@ -140,6 +144,11 @@ lt:
|
|||
title: Pavadinimas
|
||||
admin_account_action:
|
||||
include_statuses: Įtraukti praneštus įrašus į el. laišką
|
||||
type: Veiksmas
|
||||
types:
|
||||
disable: Sustabdyti
|
||||
none: Siųsti įspėjimą
|
||||
sensitive: Jautrus(-i)
|
||||
defaults:
|
||||
autofollow: Kviesti sekti tavo paskyrą
|
||||
avatar: Profilio nuotrauka
|
||||
|
|
@ -161,6 +170,7 @@ lt:
|
|||
setting_aggregate_reblogs: Grupuoti pakėlimus laiko skalėse
|
||||
setting_always_send_emails: Visada siųsti el. laiško pranešimus
|
||||
setting_auto_play_gif: Automatiškai leisti animuotų GIF
|
||||
setting_boost_modal: Kontroliuoti pasidalijimų matomumo nustatymus
|
||||
setting_default_language: Skelbimo kalba
|
||||
setting_default_quote_policy: Kas gali cituoti
|
||||
setting_default_sensitive: Visada žymėti mediją kaip jautrią
|
||||
|
|
@ -172,6 +182,7 @@ lt:
|
|||
setting_emoji_style: Jaustuko stilius
|
||||
setting_expand_spoilers: Visada išplėsti įrašus, pažymėtus turinio įspėjimais
|
||||
setting_hide_network: Slėpti savo socialinę diagramą
|
||||
setting_quick_boosting: Įjunkite greitą pasidalinimą
|
||||
setting_reduce_motion: Sumažinti judėjimą animacijose
|
||||
setting_system_font_ui: Naudoti numatytąjį sistemos šriftą
|
||||
setting_system_scrollbars_ui: Naudoti numatytąją sistemos slankjuostę
|
||||
|
|
@ -187,7 +198,7 @@ lt:
|
|||
email_domain_block:
|
||||
with_dns_records: Įtraukti MX įrašus ir domeno IP adresus
|
||||
featured_tag:
|
||||
name: Saitažodis
|
||||
name: Grotažymė
|
||||
filters:
|
||||
actions:
|
||||
blur: Slėpti mediją su įspėjimu
|
||||
|
|
@ -200,9 +211,11 @@ lt:
|
|||
content_cache_retention_period: Nuotolinio turinio saugojimo laikotarpis
|
||||
custom_css: Pasirinktinis CSS
|
||||
favicon: Svetainės piktograma
|
||||
local_topic_feed_access: Prieiga prie grotažymių ir nuorodų sienų srautų, kuriuose pateikiami vietiniai įrašai
|
||||
mascot: Pasirinktinis talismanas (pasenęs)
|
||||
min_age: Mažiausias amžiaus reikalavimas
|
||||
registrations_mode: Kas gali užsiregistruoti
|
||||
remote_topic_feed_access: Prieiga prie grotažymių ir nuorodų srautų, kuriuose pateikiami išoriniai įrašai
|
||||
require_invite_text: Reikalauti priežasties prisijungti
|
||||
show_domain_blocks_rationale: Rodyti, kodėl domenai buvo užblokuoti
|
||||
site_extended_description: Išplėstas aprašymas
|
||||
|
|
@ -224,7 +237,7 @@ lt:
|
|||
mention: Kažkas paminėjo tave
|
||||
pending_account: Reikia peržiūros naujam paskyrui
|
||||
quote: Kažkas jus paminėjo
|
||||
reblog: Kažkas pakėlė tavo įrašą
|
||||
reblog: Kažkas dalinosi tavo įrašu
|
||||
software_updates:
|
||||
label: Yra nauja Mastodon versija
|
||||
patch: Pranešti apie klaidų ištaisymo atnaujinimus
|
||||
|
|
@ -236,10 +249,10 @@ lt:
|
|||
indexable: Įtraukti profilio puslapį į paieškos variklius
|
||||
show_application: Rodyti, iš kurios programėles išsiuntei įrašą
|
||||
tag:
|
||||
listable: Leisti šį saitažodį rodyti paieškose ir pasiūlymuose
|
||||
name: Saitažodis
|
||||
trendable: Leisti šį saitažodį rodyti pagal trendus
|
||||
usable: Leisti įrašams naudoti šį saitažodį vietoje
|
||||
listable: Leisti šį grotažodį rodyti paieškose ir pasiūlymuose
|
||||
name: Grotažymė
|
||||
trendable: Leisti šią grotažymę rodyti tendencijose
|
||||
usable: Leisti įrašams naudoti šią grotažymę lokaliai
|
||||
terms_of_service:
|
||||
changelog: Kas pasikeitė?
|
||||
text: Paslaugų sąlygos
|
||||
|
|
|
|||
|
|
@ -314,7 +314,9 @@ th:
|
|||
terms_of_service_generator:
|
||||
domain: โดเมน
|
||||
user:
|
||||
date_of_birth_1i: ปี
|
||||
date_of_birth_2i: เดือน
|
||||
date_of_birth_3i: วัน
|
||||
role: บทบาท
|
||||
time_zone: โซนเวลา
|
||||
user_role:
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ th:
|
|||
email_status: สถานะอีเมล
|
||||
enable: เลิกอายัด
|
||||
enable_sign_in_token_auth: เปิดใช้งานการรับรองความถูกต้องด้วยโทเคนอีเมล
|
||||
enabled: เปิดใช้งานอยู่
|
||||
enabled: เปิดใช้งานแล้ว
|
||||
enabled_msg: เลิกอายัดบัญชีของ %{username} สำเร็จ
|
||||
followers: ผู้ติดตาม
|
||||
follows: การติดตาม
|
||||
|
|
@ -92,7 +92,7 @@ th:
|
|||
moderation:
|
||||
active: ใช้งานอยู่
|
||||
all: ทั้งหมด
|
||||
disabled: ปิดใช้งานอยู่
|
||||
disabled: ปิดใช้งานแล้ว
|
||||
pending: รอดำเนินการ
|
||||
silenced: จำกัดอยู่
|
||||
suspended: ระงับอยู่
|
||||
|
|
@ -336,11 +336,11 @@ th:
|
|||
delete: ลบ
|
||||
destroyed_msg: ทำลายอีโมโจสำเร็จ!
|
||||
disable: ปิดใช้งาน
|
||||
disabled: ปิดใช้งานอยู่
|
||||
disabled: ปิดใช้งานแล้ว
|
||||
disabled_msg: ปิดใช้งานอีโมจินั้นสำเร็จ
|
||||
emoji: อีโมจิ
|
||||
enable: เปิดใช้งาน
|
||||
enabled: เปิดใช้งานอยู่
|
||||
enabled: เปิดใช้งานแล้ว
|
||||
enabled_msg: เปิดใช้งานอีโมจินั้นสำเร็จ
|
||||
image_hint: PNG หรือ GIF สูงสุด %{size}
|
||||
list: แสดงรายการ
|
||||
|
|
@ -592,10 +592,10 @@ th:
|
|||
delete: ลบ
|
||||
description_html: "<strong>รีเลย์การติดต่อกับภายนอก</strong> เป็นเซิร์ฟเวอร์ตัวกลางที่แลกเปลี่ยนโพสต์สาธารณะจำนวนมากระหว่างเซิร์ฟเวอร์ที่บอกรับและเผยแพร่ไปยังรีเลย์ <strong>รีเลย์สามารถช่วยให้เซิร์ฟเวอร์ขนาดเล็กและขนาดกลางค้นพบเนื้อหาจากจักรวาลสหพันธ์</strong> ซึ่งมิฉะนั้นจะต้องให้ผู้ใช้ในเซิร์ฟเวอร์ติดตามผู้คนอื่น ๆ ในเซิร์ฟเวอร์ระยะไกลด้วยตนเอง"
|
||||
disable: ปิดใช้งาน
|
||||
disabled: ปิดใช้งานอยู่
|
||||
disabled: ปิดใช้งานแล้ว
|
||||
enable: เปิดใช้งาน
|
||||
enable_hint: เมื่อเปิดใช้งาน เซิร์ฟเวอร์ของคุณจะบอกรับโพสต์สาธารณะทั้งหมดจากรีเลย์นี้ และจะเริ่มส่งโพสต์สาธารณะของเซิร์ฟเวอร์นี้ไปยังรีเลย์
|
||||
enabled: เปิดใช้งานอยู่
|
||||
enabled: เปิดใช้งานแล้ว
|
||||
inbox_url: URL ของรีเลย์
|
||||
pending: กำลังรอการอนุมัติของรีเลย์
|
||||
save_and_enable: บันทึกและเปิดใช้งาน
|
||||
|
|
@ -796,9 +796,14 @@ th:
|
|||
all: ให้กับทุกคน
|
||||
disabled: ให้กับไม่มีใคร
|
||||
users: ให้กับผู้ใช้ในเซิร์ฟเวอร์ที่เข้าสู่ระบบ
|
||||
feed_access:
|
||||
modes:
|
||||
public: ทุกคน
|
||||
landing_page:
|
||||
values:
|
||||
about: เกี่ยวกับ
|
||||
local_feed: ฟีดในเซิร์ฟเวอร์
|
||||
trends: แนวโน้ม
|
||||
registrations:
|
||||
moderation_recommandation: โปรดตรวจสอบให้แน่ใจว่าคุณมีทีมการกลั่นกรองที่เพียงพอและมีปฏิกิริยาตอบสนองก่อนที่คุณจะเปิดการลงทะเบียนให้กับทุกคน!
|
||||
preamble: ควบคุมผู้ที่สามารถสร้างบัญชีในเซิร์ฟเวอร์ของคุณ
|
||||
|
|
@ -1045,7 +1050,7 @@ th:
|
|||
delete: ลบ
|
||||
description_html: "<strong>เว็บฮุค</strong> ทำให้ Mastodon สามารถส่ง <strong>การแจ้งเตือนตามเวลาจริง</strong> แบบผลักเกี่ยวกับเหตุการณ์ที่เลือกไปยังแอปพลิเคชันของคุณเอง ดังนั้นแอปพลิเคชันของคุณสามารถ <strong>ทริกเกอร์การตอบสนองได้โดยอัตโนมัติ</strong>"
|
||||
disable: ปิดใช้งาน
|
||||
disabled: ปิดใช้งานอยู่
|
||||
disabled: ปิดใช้งานแล้ว
|
||||
edit: แก้ไขปลายทาง
|
||||
empty: คุณยังไม่ได้กำหนดค่าปลายทางเว็บฮุคใด ๆ
|
||||
enable: เปิดใช้งาน
|
||||
|
|
@ -1633,6 +1638,7 @@ th:
|
|||
self_vote: คุณไม่สามารถลงคะแนนในการสำรวจความคิดเห็นของคุณเอง
|
||||
too_few_options: ต้องมีมากกว่าหนึ่งรายการ
|
||||
too_many_options: ไม่สามารถมีมากกว่า %{max} รายการ
|
||||
vote: ลงคะแนน
|
||||
preferences:
|
||||
other: อื่น ๆ
|
||||
posting_defaults: ค่าเริ่มต้นการโพสต์
|
||||
|
|
@ -1800,9 +1806,16 @@ th:
|
|||
limit: คุณได้ปักหมุดโพสต์ถึงจำนวนสูงสุดไปแล้ว
|
||||
ownership: ไม่สามารถปักหมุดโพสต์ของคนอื่น
|
||||
reblog: ไม่สามารถปักหมุดการดัน
|
||||
quote_policies:
|
||||
followers: ผู้ติดตามเท่านั้น
|
||||
nobody: แค่ฉัน
|
||||
public: ใครก็ตาม
|
||||
title: '%{name}: "%{quote}"'
|
||||
visibilities:
|
||||
direct: การกล่าวถึงแบบส่วนตัว
|
||||
private: ผู้ติดตามเท่านั้น
|
||||
public: สาธารณะ
|
||||
unlisted_long: ซ่อนจากผลลัพธ์การค้นหา, กำลังนิยม และเส้นเวลาสาธารณะของ Mastodon
|
||||
statuses_cleanup:
|
||||
enabled: ลบโพสต์เก่าโดยอัตโนมัติ
|
||||
enabled_hint: ลบโพสต์ของคุณโดยอัตโนมัติเมื่อโพสต์ถึงค่าเกณฑ์อายุที่ระบุ เว้นแต่โพสต์ตรงกับหนึ่งในข้อยกเว้นด้านล่าง
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ namespace :api, format: false do
|
|||
|
||||
# Experimental JSON / REST API
|
||||
namespace :v1_alpha do
|
||||
resources :accounts, only: [] do
|
||||
resources :collections, only: [:index]
|
||||
end
|
||||
|
||||
resources :async_refreshes, only: :show
|
||||
|
||||
resources :collections, only: [:show, :create, :update, :destroy]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddItemCountToCollections < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_column :collections, :item_count, :integer, default: 0, null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_12_02_140424) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_12_09_093813) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
||||
|
|
@ -380,6 +380,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_02_140424) do
|
|||
t.integer "original_number_of_items"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "item_count", default: 0, null: false
|
||||
t.index ["account_id"], name: "index_collections_on_account_id"
|
||||
t.index ["tag_id"], name: "index_collections_on_tag_id"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ services:
|
|||
web:
|
||||
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
||||
# build: .
|
||||
image: ghcr.io/mastodon/mastodon:v4.5.2
|
||||
image: ghcr.io/mastodon/mastodon:v4.5.3
|
||||
restart: always
|
||||
env_file: .env.production
|
||||
command: bundle exec puma -C config/puma.rb
|
||||
|
|
@ -83,7 +83,7 @@ services:
|
|||
# build:
|
||||
# dockerfile: ./streaming/Dockerfile
|
||||
# context: .
|
||||
image: ghcr.io/mastodon/mastodon-streaming:v4.5.2
|
||||
image: ghcr.io/mastodon/mastodon-streaming:v4.5.3
|
||||
restart: always
|
||||
env_file: .env.production
|
||||
command: node ./streaming/index.js
|
||||
|
|
@ -102,7 +102,7 @@ services:
|
|||
sidekiq:
|
||||
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
||||
# build: .
|
||||
image: ghcr.io/mastodon/mastodon:v4.5.2
|
||||
image: ghcr.io/mastodon/mastodon:v4.5.3
|
||||
restart: always
|
||||
env_file: .env.production
|
||||
command: bundle exec sidekiq
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue