mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-11 14:30:35 +00:00
Merge commit 'a13756148d353c7479f68e65a210f6d88d26c785' into glitch-soc/merge-upstream
Conflicts: - `app/views/layouts/embedded.html.haml`: Upstream made a change to javascript tags next to lines changed in glitch-soc because of the theming system. Added the javascript entrypoint upstream added. - `app/views/layouts/error.html.haml`: Upstream made a change to javascript tags next to lines changed in glitch-soc because of the theming system. Added the javascript entrypoint upstream added.
This commit is contained in:
3
.github/renovate.json5
vendored
3
.github/renovate.json5
vendored
@@ -28,6 +28,9 @@
|
||||
// react-router: Requires manual upgrade
|
||||
'history',
|
||||
'react-router-dom',
|
||||
|
||||
// react-spring: Requires manual upgrade when upgrading react
|
||||
'@react-spring/web',
|
||||
],
|
||||
matchUpdateTypes: ['major'],
|
||||
dependencyDashboardApproval: true,
|
||||
|
||||
@@ -70,13 +70,6 @@ Style/OptionalBooleanParameter:
|
||||
- 'app/workers/domain_block_worker.rb'
|
||||
- 'app/workers/unfollow_follow_worker.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: short, verbose
|
||||
Style/PreferredHashMethods:
|
||||
Exclude:
|
||||
- 'config/initializers/paperclip.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
Style/RedundantConstantBase:
|
||||
Exclude:
|
||||
|
||||
@@ -62,6 +62,11 @@ Bug reports and feature suggestions must use descriptive and concise titles and
|
||||
be submitted to [GitHub Issues]. Please use the search function to make sure
|
||||
there are not duplicate bug reports or feature requests.
|
||||
|
||||
## Security Issues
|
||||
|
||||
If you believe you have identified a security issue in Mastodon or our own apps,
|
||||
check [SECURITY].
|
||||
|
||||
## Translations
|
||||
|
||||
Translations are community contributed via [Crowdin]. They are periodically
|
||||
@@ -124,3 +129,4 @@ and API docs. Improvements are made via PRs to the [documentation repository].
|
||||
[GitHub Issues]: https://github.com/mastodon/mastodon/issues
|
||||
[keepachangelog]: https://keepachangelog.com/en/1.0.0/
|
||||
[Mastodon documentation]: https://docs.joinmastodon.org
|
||||
[SECURITY]: SECURITY.md
|
||||
|
||||
2
Gemfile
2
Gemfile
@@ -62,7 +62,7 @@ gem 'inline_svg'
|
||||
gem 'irb', '~> 1.8'
|
||||
gem 'kaminari', '~> 1.2'
|
||||
gem 'link_header', '~> 0.0'
|
||||
gem 'linzer', '~> 0.6.1'
|
||||
gem 'linzer', '~> 0.7.2'
|
||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||
gem 'mime-types', '~> 3.7.0', require: 'mime/types/columnar'
|
||||
gem 'mutex_m'
|
||||
|
||||
17
Gemfile.lock
17
Gemfile.lock
@@ -148,6 +148,7 @@ GEM
|
||||
case_transform (0.2)
|
||||
activesupport
|
||||
cbor (0.5.9.8)
|
||||
cgi (0.4.2)
|
||||
charlock_holmes (0.7.9)
|
||||
chewy (7.6.0)
|
||||
activesupport (>= 5.2)
|
||||
@@ -262,6 +263,7 @@ GEM
|
||||
fog-core (~> 2.1)
|
||||
fog-json (>= 1.0)
|
||||
formatador (1.1.0)
|
||||
forwardable (1.3.3)
|
||||
fugit (1.11.1)
|
||||
et-orbi (~> 1, >= 1.2.11)
|
||||
raabro (~> 1.4)
|
||||
@@ -396,7 +398,11 @@ GEM
|
||||
rexml
|
||||
link_header (0.0.8)
|
||||
lint_roller (1.1.0)
|
||||
linzer (0.6.5)
|
||||
linzer (0.7.2)
|
||||
cgi (~> 0.4.2)
|
||||
forwardable (~> 1.3, >= 1.3.3)
|
||||
logger (~> 1.7, >= 1.7.0)
|
||||
net-http (~> 0.6.0)
|
||||
openssl (~> 3.0, >= 3.0.0)
|
||||
rack (>= 2.2, < 4.0)
|
||||
starry (~> 0.2)
|
||||
@@ -628,7 +634,7 @@ GEM
|
||||
activesupport (>= 3.0.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.8.1)
|
||||
rack (2.2.15)
|
||||
rack (2.2.16)
|
||||
rack-attack (6.7.0)
|
||||
rack (>= 1.0, < 4)
|
||||
rack-cors (2.0.2)
|
||||
@@ -742,7 +748,7 @@ GEM
|
||||
rspec-mocks (~> 3.0)
|
||||
sidekiq (>= 5, < 9)
|
||||
rspec-support (3.13.3)
|
||||
rubocop (1.75.6)
|
||||
rubocop (1.75.7)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
@@ -779,7 +785,8 @@ GEM
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.72, >= 1.72.1)
|
||||
rubocop-rspec (~> 3.5)
|
||||
ruby-prof (1.7.1)
|
||||
ruby-prof (1.7.2)
|
||||
base64
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-saml (1.18.0)
|
||||
nokogiri (>= 1.13.10)
|
||||
@@ -993,7 +1000,7 @@ DEPENDENCIES
|
||||
letter_opener (~> 1.8)
|
||||
letter_opener_web (~> 3.0)
|
||||
link_header (~> 0.0)
|
||||
linzer (~> 0.6.1)
|
||||
linzer (~> 0.7.2)
|
||||
lograge (~> 0.12)
|
||||
mail (~> 2.8)
|
||||
mario-redis-lock (~> 1.2)
|
||||
|
||||
@@ -93,12 +93,12 @@ accepted into Mastodon, you can request to be paid through our [OpenCollective].
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2016-2024 Eugen Rochko (+ [`mastodon authors`](AUTHORS.md))
|
||||
Copyright (c) 2016-2025 Eugen Rochko (+ [`mastodon authors`](AUTHORS.md))
|
||||
|
||||
Licensed under GNU Affero General Public License as stated in the [LICENSE](LICENSE):
|
||||
|
||||
```
|
||||
Copyright (c) 2016-2024 Eugen Rochko & other Mastodon contributors
|
||||
Copyright (c) 2016-2025 Eugen Rochko & other Mastodon contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License as published by the Free
|
||||
|
||||
@@ -42,37 +42,22 @@ class Api::Fasp::BaseController < ApplicationController
|
||||
end
|
||||
|
||||
def validate_signature!
|
||||
signature_input = request.headers['signature-input']&.encode('UTF-8')
|
||||
raise Error, 'signature-input is missing' if signature_input.blank?
|
||||
raise Error, 'signature-input is missing' if request.headers['signature-input'].blank?
|
||||
|
||||
provider = nil
|
||||
|
||||
Linzer.verify!(request.rack_request, no_older_than: 5.minutes) do |keyid|
|
||||
provider = Fasp::Provider.find(keyid)
|
||||
Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
|
||||
end
|
||||
|
||||
keyid = signature_input.match(KEYID_PATTERN)[1]
|
||||
provider = Fasp::Provider.find(keyid)
|
||||
linzer_request = Linzer.new_request(
|
||||
request.method,
|
||||
request.original_url,
|
||||
{},
|
||||
{
|
||||
'content-digest' => request.headers['content-digest'],
|
||||
'signature-input' => signature_input,
|
||||
'signature' => request.headers['signature'],
|
||||
}
|
||||
)
|
||||
message = Linzer::Message.new(linzer_request)
|
||||
key = Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
|
||||
signature = Linzer::Signature.build(message.headers)
|
||||
Linzer.verify(key, message, signature)
|
||||
@current_provider = provider
|
||||
end
|
||||
|
||||
def sign_response
|
||||
response.headers['content-digest'] = "sha-256=:#{OpenSSL::Digest.base64digest('sha256', response.body || '')}:"
|
||||
|
||||
linzer_response = Linzer.new_response(response.body, response.status, { 'content-digest' => response.headers['content-digest'] })
|
||||
message = Linzer::Message.new(linzer_response)
|
||||
key = Linzer.new_ed25519_key(current_provider.server_private_key_pem)
|
||||
signature = Linzer.sign(key, message, %w(@status content-digest))
|
||||
|
||||
response.headers.merge!(signature.to_h)
|
||||
Linzer.sign!(response, key:, components: %w(@status content-digest))
|
||||
end
|
||||
|
||||
def check_fasp_enabled
|
||||
|
||||
@@ -69,6 +69,10 @@ export function importFetchedStatuses(statuses) {
|
||||
processStatus(status.reblog);
|
||||
}
|
||||
|
||||
if (status.quote?.quoted_status) {
|
||||
processStatus(status.quote.quoted_status);
|
||||
}
|
||||
|
||||
if (status.poll?.id) {
|
||||
pushUnique(polls, createPollFromServerJSON(status.poll, getState().polls[status.poll.id]));
|
||||
}
|
||||
|
||||
@@ -23,12 +23,20 @@ export function normalizeFilterResult(result) {
|
||||
|
||||
export function normalizeStatus(status, normalOldStatus) {
|
||||
const normalStatus = { ...status };
|
||||
|
||||
normalStatus.account = status.account.id;
|
||||
|
||||
if (status.reblog && status.reblog.id) {
|
||||
normalStatus.reblog = status.reblog.id;
|
||||
}
|
||||
|
||||
if (status.quote?.quoted_status ?? status.quote?.quoted_status_id) {
|
||||
normalStatus.quote = {
|
||||
...status.quote,
|
||||
quoted_status: status.quote.quoted_status?.id ?? status.quote?.quoted_status_id,
|
||||
};
|
||||
}
|
||||
|
||||
if (status.poll && status.poll.id) {
|
||||
normalStatus.poll = status.poll.id;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
import { VerifiedBadge } from 'mastodon/components/verified_badge';
|
||||
import { useIdentity } from 'mastodon/identity_context';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import type { MenuItem } from 'mastodon/models/dropdown_menu';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
@@ -70,10 +71,12 @@ export const Account: React.FC<{
|
||||
withBio?: boolean;
|
||||
}> = ({ id, size = 46, hidden, minimal, defaultAction, withBio }) => {
|
||||
const intl = useIntl();
|
||||
const { signedIn } = useIdentity();
|
||||
const account = useAppSelector((state) => state.accounts.get(id));
|
||||
const relationship = useAppSelector((state) => state.relationships.get(id));
|
||||
const dispatch = useAppDispatch();
|
||||
const accountUrl = account?.url;
|
||||
const isRemote = account?.acct !== account?.username;
|
||||
|
||||
const handleBlock = useCallback(() => {
|
||||
if (relationship?.blocking) {
|
||||
@@ -116,66 +119,74 @@ export const Account: React.FC<{
|
||||
},
|
||||
];
|
||||
} else if (defaultAction !== 'block') {
|
||||
const handleAddToLists = () => {
|
||||
const openAddToListModal = () => {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'LIST_ADDER',
|
||||
modalProps: {
|
||||
accountId: id,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
if (relationship?.following || relationship?.requested || id === me) {
|
||||
openAddToListModal();
|
||||
} else {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'CONFIRM_FOLLOW_TO_LIST',
|
||||
modalProps: {
|
||||
accountId: id,
|
||||
onConfirm: () => {
|
||||
apiFollowAccount(id)
|
||||
.then((relationship) => {
|
||||
dispatch(
|
||||
followAccountSuccess({
|
||||
relationship,
|
||||
alreadyFollowing: false,
|
||||
}),
|
||||
);
|
||||
openAddToListModal();
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
dispatch(showAlertForError(err));
|
||||
});
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
arr = [];
|
||||
|
||||
arr = [
|
||||
{
|
||||
if (isRemote && accountUrl) {
|
||||
arr.push({
|
||||
text: intl.formatMessage(messages.openOriginalPage),
|
||||
href: accountUrl,
|
||||
});
|
||||
}
|
||||
|
||||
if (signedIn) {
|
||||
const handleAddToLists = () => {
|
||||
const openAddToListModal = () => {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'LIST_ADDER',
|
||||
modalProps: {
|
||||
accountId: id,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
if (relationship?.following || relationship?.requested || id === me) {
|
||||
openAddToListModal();
|
||||
} else {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'CONFIRM_FOLLOW_TO_LIST',
|
||||
modalProps: {
|
||||
accountId: id,
|
||||
onConfirm: () => {
|
||||
apiFollowAccount(id)
|
||||
.then((relationship) => {
|
||||
dispatch(
|
||||
followAccountSuccess({
|
||||
relationship,
|
||||
alreadyFollowing: false,
|
||||
}),
|
||||
);
|
||||
openAddToListModal();
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
dispatch(showAlertForError(err));
|
||||
});
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
arr.push({
|
||||
text: intl.formatMessage(messages.addToLists),
|
||||
action: handleAddToLists,
|
||||
},
|
||||
];
|
||||
|
||||
if (accountUrl) {
|
||||
arr.unshift(
|
||||
{
|
||||
text: intl.formatMessage(messages.openOriginalPage),
|
||||
href: accountUrl,
|
||||
},
|
||||
null,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}, [dispatch, intl, id, accountUrl, relationship, defaultAction]);
|
||||
}, [
|
||||
dispatch,
|
||||
intl,
|
||||
id,
|
||||
accountUrl,
|
||||
relationship,
|
||||
defaultAction,
|
||||
isRemote,
|
||||
signedIn,
|
||||
]);
|
||||
|
||||
if (hidden) {
|
||||
return (
|
||||
|
||||
@@ -7,10 +7,16 @@ import classNames from 'classnames';
|
||||
|
||||
export const AvatarGroup: React.FC<{
|
||||
compact?: boolean;
|
||||
avatarHeight?: number;
|
||||
children: React.ReactNode;
|
||||
}> = ({ children, compact = false }) => (
|
||||
}> = ({ children, compact = false, avatarHeight }) => (
|
||||
<div
|
||||
className={classNames('avatar-group', { 'avatar-group--compact': compact })}
|
||||
style={
|
||||
avatarHeight
|
||||
? ({ '--avatar-height': `${avatarHeight}px` } as React.CSSProperties)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -43,3 +43,17 @@ export const FollowersCounter = (
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export const FollowersYouKnowCounter = (
|
||||
displayNumber: React.ReactNode,
|
||||
pluralReady: number,
|
||||
) => (
|
||||
<FormattedMessage
|
||||
id='account.followers_you_know_counter'
|
||||
defaultMessage='{counter} you know'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: <strong>{displayNumber}</strong>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -9,11 +9,16 @@ import { fetchAccount } from 'mastodon/actions/accounts';
|
||||
import { AccountBio } from 'mastodon/components/account_bio';
|
||||
import { AccountFields } from 'mastodon/components/account_fields';
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { FollowersCounter } from 'mastodon/components/counters';
|
||||
import { AvatarGroup } from 'mastodon/components/avatar_group';
|
||||
import {
|
||||
FollowersCounter,
|
||||
FollowersYouKnowCounter,
|
||||
} from 'mastodon/components/counters';
|
||||
import { DisplayName } from 'mastodon/components/display_name';
|
||||
import { FollowButton } from 'mastodon/components/follow_button';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
import { useFetchFamiliarFollowers } from 'mastodon/features/account_timeline/hooks/familiar_followers';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
|
||||
@@ -38,6 +43,8 @@ export const HoverCardAccount = forwardRef<
|
||||
}
|
||||
}, [dispatch, accountId, account]);
|
||||
|
||||
const { familiarFollowers } = useFetchFamiliarFollowers({ accountId });
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@@ -73,11 +80,27 @@ export const HoverCardAccount = forwardRef<
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='hover-card__number'>
|
||||
<div className='hover-card__numbers'>
|
||||
<ShortNumber
|
||||
value={account.followers_count}
|
||||
renderer={FollowersCounter}
|
||||
/>
|
||||
{familiarFollowers.length > 0 && (
|
||||
<>
|
||||
·
|
||||
<div className='hover-card__familiar-followers'>
|
||||
<ShortNumber
|
||||
value={familiarFollowers.length}
|
||||
renderer={FollowersYouKnowCounter}
|
||||
/>
|
||||
<AvatarGroup compact>
|
||||
{familiarFollowers.slice(0, 3).map((account) => (
|
||||
<Avatar key={account.id} account={account} size={22} />
|
||||
))}
|
||||
</AvatarGroup>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FollowButton accountId={accountId} />
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { IconLogo } from 'mastodon/components/logo';
|
||||
import { AuthorLink } from 'mastodon/features/explore/components/author_link';
|
||||
|
||||
export const MoreFromAuthor = ({ accountId }) => (
|
||||
<div className='more-from-author'>
|
||||
<IconLogo />
|
||||
<FormattedMessage id='link_preview.more_from_author' defaultMessage='More from {name}' values={{ name: <AuthorLink accountId={accountId} /> }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
MoreFromAuthor.propTypes = {
|
||||
accountId: PropTypes.string.isRequired,
|
||||
};
|
||||
21
app/javascript/mastodon/components/more_from_author.tsx
Normal file
21
app/javascript/mastodon/components/more_from_author.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { IconLogo } from 'mastodon/components/logo';
|
||||
import { AuthorLink } from 'mastodon/features/explore/components/author_link';
|
||||
|
||||
export const MoreFromAuthor: React.FC<{ accountId: string }> = ({
|
||||
accountId,
|
||||
}) => (
|
||||
<FormattedMessage
|
||||
id='link_preview.more_from_author'
|
||||
defaultMessage='More from {name}'
|
||||
values={{ name: <AuthorLink accountId={accountId} /> }}
|
||||
>
|
||||
{(chunks) => (
|
||||
<div className='more-from-author'>
|
||||
<IconLogo />
|
||||
{chunks}
|
||||
</div>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
);
|
||||
@@ -5,14 +5,12 @@ import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||
import { ContentWarning } from 'mastodon/components/content_warning';
|
||||
import { FilterWarning } from 'mastodon/components/filter_warning';
|
||||
@@ -88,6 +86,7 @@ class Status extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map,
|
||||
account: ImmutablePropTypes.record,
|
||||
children: PropTypes.node,
|
||||
previousId: PropTypes.string,
|
||||
nextInReplyToId: PropTypes.string,
|
||||
rootId: PropTypes.string,
|
||||
@@ -115,6 +114,7 @@ class Status extends ImmutablePureComponent {
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
showThread: PropTypes.bool,
|
||||
isQuotedPost: PropTypes.bool,
|
||||
getScrollPosition: PropTypes.func,
|
||||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth: PropTypes.func,
|
||||
@@ -372,7 +372,7 @@ class Status extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, hidden, featured, unfocusable, unread, showThread, scrollKey, pictureInPicture, previousId, nextInReplyToId, rootId, skipPrepend, avatarSize = 46 } = this.props;
|
||||
const { intl, hidden, featured, unfocusable, unread, showThread, isQuotedPost = false, scrollKey, pictureInPicture, previousId, nextInReplyToId, rootId, skipPrepend, avatarSize = 46, children } = this.props;
|
||||
|
||||
let { status, account, ...other } = this.props;
|
||||
|
||||
@@ -519,7 +519,7 @@ class Status extends ImmutablePureComponent {
|
||||
</Bundle>
|
||||
);
|
||||
}
|
||||
} else if (status.get('card')) {
|
||||
} else if (status.get('card') && !status.get('quote')) {
|
||||
media = (
|
||||
<Card
|
||||
onOpenMedia={this.handleOpenMedia}
|
||||
@@ -543,7 +543,19 @@ class Status extends ImmutablePureComponent {
|
||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted || unfocusable ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef} data-nosnippet={status.getIn(['account', 'noindex'], true) || undefined}>
|
||||
{!skipPrepend && prepend}
|
||||
|
||||
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), 'status--in-thread': !!rootId, 'status--first-in-thread': previousId && (!connectUp || connectToRoot), muted: this.props.muted })} data-id={status.get('id')}>
|
||||
<div
|
||||
className={
|
||||
classNames('status', `status-${status.get('visibility')}`,
|
||||
{
|
||||
'status-reply': !!status.get('in_reply_to_id'),
|
||||
'status--in-thread': !!rootId,
|
||||
'status--first-in-thread': previousId && (!connectUp || connectToRoot), muted: this.props.muted,
|
||||
'status--is-quote': isQuotedPost,
|
||||
'status--has-quote': !!status.get('quote'),
|
||||
})
|
||||
}
|
||||
data-id={status.get('id')}
|
||||
>
|
||||
{(connectReply || connectUp || connectToRoot) && <div className={classNames('status__line', { 'status__line--full': connectReply, 'status__line--first': !status.get('in_reply_to_id') && !connectToRoot })} />}
|
||||
|
||||
<div onClick={this.handleHeaderClick} onAuxClick={this.handleHeaderClick} className='status__info'>
|
||||
@@ -576,12 +588,16 @@ class Status extends ImmutablePureComponent {
|
||||
{...statusContentProps}
|
||||
/>
|
||||
|
||||
{children}
|
||||
|
||||
{media}
|
||||
{hashtagBar}
|
||||
</>
|
||||
)}
|
||||
|
||||
<StatusActionBar scrollKey={scrollKey} status={status} account={account} {...other} />
|
||||
{!isQuotedPost &&
|
||||
<StatusActionBar scrollKey={scrollKey} status={status} account={account} {...other} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { TIMELINE_GAP, TIMELINE_SUGGESTIONS } from 'mastodon/actions/timelines';
|
||||
import { RegenerationIndicator } from 'mastodon/components/regeneration_indicator';
|
||||
import { InlineFollowSuggestions } from 'mastodon/features/home_timeline/components/inline_follow_suggestions';
|
||||
|
||||
import StatusContainer from '../containers/status_container';
|
||||
import { StatusQuoteManager } from '../components/status_quoted';
|
||||
|
||||
import { LoadGap } from './load_gap';
|
||||
import ScrollableList from './scrollable_list';
|
||||
@@ -113,7 +113,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
key={statusId}
|
||||
id={statusId}
|
||||
onMoveUp={this.handleMoveUp}
|
||||
@@ -130,7 +130,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
|
||||
if (scrollableContent && featuredStatusIds) {
|
||||
scrollableContent = featuredStatusIds.map(statusId => (
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
key={`f-${statusId}`}
|
||||
id={statusId}
|
||||
featured
|
||||
|
||||
208
app/javascript/mastodon/components/status_quoted.tsx
Normal file
208
app/javascript/mastodon/components/status_quoted.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import ArticleIcon from '@/material-icons/400-24px/article.svg?react';
|
||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import StatusContainer from 'mastodon/containers/status_container';
|
||||
import type { Status } from 'mastodon/models/status';
|
||||
import type { RootState } from 'mastodon/store';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
|
||||
import QuoteIcon from '../../images/quote.svg?react';
|
||||
import { makeGetStatus } from '../selectors';
|
||||
|
||||
const MAX_QUOTE_POSTS_NESTING_LEVEL = 1;
|
||||
|
||||
const QuoteWrapper: React.FC<{
|
||||
isError?: boolean;
|
||||
children: React.ReactElement;
|
||||
}> = ({ isError, children }) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames('status__quote', {
|
||||
'status__quote--error': isError,
|
||||
})}
|
||||
>
|
||||
<Icon id='quote' icon={QuoteIcon} className='status__quote-icon' />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const QuoteLink: React.FC<{
|
||||
status: Status;
|
||||
}> = ({ status }) => {
|
||||
const accountId = status.get('account') as string;
|
||||
const account = useAppSelector((state) =>
|
||||
accountId ? state.accounts.get(accountId) : undefined,
|
||||
);
|
||||
|
||||
const quoteAuthorName = account?.display_name_html;
|
||||
|
||||
if (!quoteAuthorName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const quoteAuthorElement = (
|
||||
<span dangerouslySetInnerHTML={{ __html: quoteAuthorName }} />
|
||||
);
|
||||
const quoteUrl = `/@${account.get('acct')}/${status.get('id') as string}`;
|
||||
|
||||
return (
|
||||
<Link to={quoteUrl} className='status__quote-author-button'>
|
||||
<FormattedMessage
|
||||
id='status.quote_post_author'
|
||||
defaultMessage='Post by {name}'
|
||||
values={{ name: quoteAuthorElement }}
|
||||
/>
|
||||
<Icon id='chevron_right' icon={ChevronRightIcon} />
|
||||
<Icon id='article' icon={ArticleIcon} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
type QuoteMap = ImmutableMap<'state' | 'quoted_status', string | null>;
|
||||
type GetStatusSelector = (
|
||||
state: RootState,
|
||||
props: { id?: string | null; contextType?: string },
|
||||
) => Status | null;
|
||||
|
||||
export const QuotedStatus: React.FC<{
|
||||
quote: QuoteMap;
|
||||
contextType?: string;
|
||||
variant?: 'full' | 'link';
|
||||
nestingLevel?: number;
|
||||
}> = ({ quote, contextType, nestingLevel = 1, variant = 'full' }) => {
|
||||
const quotedStatusId = quote.get('quoted_status');
|
||||
const quoteState = quote.get('state');
|
||||
const status = useAppSelector((state) =>
|
||||
quotedStatusId ? state.statuses.get(quotedStatusId) : undefined,
|
||||
);
|
||||
let quoteError: React.ReactNode = null;
|
||||
|
||||
// In order to find out whether the quoted post should be completely hidden
|
||||
// due to a matching filter, we run it through the selector used by `status_container`.
|
||||
// If this returns null even though `status` exists, it's because it's filtered.
|
||||
const getStatus = useMemo(() => makeGetStatus(), []) as GetStatusSelector;
|
||||
const statusWithExtraData = useAppSelector((state) =>
|
||||
getStatus(state, { id: quotedStatusId, contextType }),
|
||||
);
|
||||
const isFilteredAndHidden = status && statusWithExtraData === null;
|
||||
|
||||
if (isFilteredAndHidden) {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.filtered'
|
||||
defaultMessage='Hidden due to one of your filters'
|
||||
/>
|
||||
);
|
||||
} else if (quoteState === 'deleted') {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.removed'
|
||||
defaultMessage='This post was removed by its author.'
|
||||
/>
|
||||
);
|
||||
} else if (quoteState === 'unauthorized') {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.unauthorized'
|
||||
defaultMessage='This post cannot be displayed as you are not authorized to view it.'
|
||||
/>
|
||||
);
|
||||
} else if (quoteState === 'pending') {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.pending_approval'
|
||||
defaultMessage='This post is pending approval from the original author.'
|
||||
/>
|
||||
);
|
||||
} else if (quoteState === 'rejected' || quoteState === 'revoked') {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.rejected'
|
||||
defaultMessage='This post cannot be displayed as the original author does not allow it to be quoted.'
|
||||
/>
|
||||
);
|
||||
} else if (!status || !quotedStatusId) {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.not_found'
|
||||
defaultMessage='This post cannot be displayed.'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (quoteError) {
|
||||
return <QuoteWrapper isError>{quoteError}</QuoteWrapper>;
|
||||
}
|
||||
|
||||
if (variant === 'link' && status) {
|
||||
return <QuoteLink status={status} />;
|
||||
}
|
||||
|
||||
const childQuote = status?.get('quote') as QuoteMap | undefined;
|
||||
const canRenderChildQuote =
|
||||
childQuote && nestingLevel <= MAX_QUOTE_POSTS_NESTING_LEVEL;
|
||||
|
||||
return (
|
||||
<QuoteWrapper>
|
||||
{/* @ts-expect-error Status is not yet typed */}
|
||||
<StatusContainer
|
||||
isQuotedPost
|
||||
id={quotedStatusId}
|
||||
contextType={contextType}
|
||||
avatarSize={40}
|
||||
>
|
||||
{canRenderChildQuote && (
|
||||
<QuotedStatus
|
||||
quote={childQuote}
|
||||
contextType={contextType}
|
||||
variant={
|
||||
nestingLevel === MAX_QUOTE_POSTS_NESTING_LEVEL ? 'link' : 'full'
|
||||
}
|
||||
nestingLevel={nestingLevel + 1}
|
||||
/>
|
||||
)}
|
||||
</StatusContainer>
|
||||
</QuoteWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
interface StatusQuoteManagerProps {
|
||||
id: string;
|
||||
contextType?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* This wrapper component takes a status ID and, if the associated status
|
||||
* is a quote post, it renders the quote into `StatusContainer` as a child.
|
||||
* It passes all other props through to `StatusContainer`.
|
||||
*/
|
||||
|
||||
export const StatusQuoteManager = (props: StatusQuoteManagerProps) => {
|
||||
const status = useAppSelector((state) => {
|
||||
const status = state.statuses.get(props.id);
|
||||
const reblogId = status?.get('reblog') as string | undefined;
|
||||
return reblogId ? state.statuses.get(reblogId) : status;
|
||||
});
|
||||
const quote = status?.get('quote') as QuoteMap | undefined;
|
||||
|
||||
if (quote) {
|
||||
return (
|
||||
<StatusContainer {...props}>
|
||||
<QuotedStatus quote={quote} contextType={props.contextType} />
|
||||
</StatusContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return <StatusContainer {...props} />;
|
||||
};
|
||||
@@ -171,8 +171,8 @@ class About extends PureComponent {
|
||||
) : (
|
||||
<ol className='rules-list'>
|
||||
{server.get('rules').map(rule => {
|
||||
const text = rule.getIn(['translations', locale, 'text']) || rule.get('text');
|
||||
const hint = rule.getIn(['translations', locale, 'hint']) || rule.get('hint');
|
||||
const text = rule.getIn(['translations', locale, 'text']) || rule.getIn(['translations', locale.split('-')[0], 'text']) || rule.get('text');
|
||||
const hint = rule.getIn(['translations', locale, 'hint']) || rule.getIn(['translations', locale.split('-')[0], 'hint']) || rule.get('hint');
|
||||
return (
|
||||
<li key={rule.get('id')}>
|
||||
<div className='rules-list__text'>{text}</div>
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Account } from 'mastodon/components/account';
|
||||
import { ColumnBackButton } from 'mastodon/components/column_back_button';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { RemoteHint } from 'mastodon/components/remote_hint';
|
||||
import StatusContainer from 'mastodon/containers/status_container';
|
||||
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
||||
import { AccountHeader } from 'mastodon/features/account_timeline/components/account_header';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
import Column from 'mastodon/features/ui/components/column';
|
||||
@@ -142,9 +142,8 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
|
||||
/>
|
||||
</h4>
|
||||
{featuredStatusIds.map((statusId) => (
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
key={`f-${statusId}`}
|
||||
// @ts-expect-error inferred props are wrong
|
||||
id={statusId}
|
||||
contextType='account'
|
||||
/>
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { fetchAccountsFamiliarFollowers } from '@/mastodon/actions/accounts_familiar_followers';
|
||||
import { Avatar } from '@/mastodon/components/avatar';
|
||||
import { AvatarGroup } from '@/mastodon/components/avatar_group';
|
||||
import type { Account } from '@/mastodon/models/account';
|
||||
import { getAccountFamiliarFollowers } from '@/mastodon/selectors/accounts';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
|
||||
import { useFetchFamiliarFollowers } from '../hooks/familiar_followers';
|
||||
|
||||
const AccountLink: React.FC<{ account?: Account }> = ({ account }) => {
|
||||
if (!account) {
|
||||
@@ -64,20 +61,11 @@ const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({
|
||||
export const FamiliarFollowers: React.FC<{ accountId: string }> = ({
|
||||
accountId,
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const familiarFollowers = useAppSelector((state) =>
|
||||
getAccountFamiliarFollowers(state, accountId),
|
||||
);
|
||||
const { familiarFollowers, isLoading } = useFetchFamiliarFollowers({
|
||||
accountId,
|
||||
});
|
||||
|
||||
const hasNoData = familiarFollowers === null;
|
||||
|
||||
useEffect(() => {
|
||||
if (hasNoData) {
|
||||
void dispatch(fetchAccountsFamiliarFollowers({ id: accountId }));
|
||||
}
|
||||
}, [dispatch, accountId, hasNoData]);
|
||||
|
||||
if (hasNoData || familiarFollowers.length === 0) {
|
||||
if (isLoading || familiarFollowers.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { fetchAccountsFamiliarFollowers } from '@/mastodon/actions/accounts_familiar_followers';
|
||||
import { getAccountFamiliarFollowers } from '@/mastodon/selectors/accounts';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
|
||||
export const useFetchFamiliarFollowers = ({
|
||||
accountId,
|
||||
}: {
|
||||
accountId?: string;
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const familiarFollowers = useAppSelector((state) =>
|
||||
accountId ? getAccountFamiliarFollowers(state, accountId) : null,
|
||||
);
|
||||
|
||||
const hasNoData = familiarFollowers === null;
|
||||
|
||||
useEffect(() => {
|
||||
if (hasNoData && accountId && accountId !== me) {
|
||||
void dispatch(fetchAccountsFamiliarFollowers({ id: accountId }));
|
||||
}
|
||||
}, [dispatch, accountId, hasNoData]);
|
||||
|
||||
return {
|
||||
familiarFollowers: hasNoData ? [] : familiarFollowers,
|
||||
isLoading: hasNoData,
|
||||
};
|
||||
};
|
||||
@@ -20,7 +20,7 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import { Account } from 'mastodon/components/account';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import StatusContainer from 'mastodon/containers/status_container';
|
||||
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
@@ -175,7 +175,7 @@ class Notification extends ImmutablePureComponent {
|
||||
|
||||
renderMention (notification) {
|
||||
return (
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
id={notification.get('status')}
|
||||
withDismiss
|
||||
hidden={this.props.hidden}
|
||||
@@ -205,7 +205,7 @@ class Notification extends ImmutablePureComponent {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
muted
|
||||
@@ -235,7 +235,7 @@ class Notification extends ImmutablePureComponent {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
muted
|
||||
@@ -269,7 +269,7 @@ class Notification extends ImmutablePureComponent {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
contextType='notifications'
|
||||
@@ -304,7 +304,7 @@ class Notification extends ImmutablePureComponent {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
contextType='notifications'
|
||||
@@ -345,7 +345,7 @@ class Notification extends ImmutablePureComponent {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
id={notification.get('status')}
|
||||
account={account}
|
||||
contextType='notifications'
|
||||
|
||||
@@ -18,12 +18,14 @@ import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
import { DisplayedName } from './displayed_name';
|
||||
import { EmbeddedStatus } from './embedded_status';
|
||||
|
||||
const AVATAR_SIZE = 28;
|
||||
|
||||
export const AvatarById: React.FC<{ accountId: string }> = ({ accountId }) => {
|
||||
const account = useAppSelector((state) => state.accounts.get(accountId));
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
return <Avatar withLink account={account} size={28} />;
|
||||
return <Avatar withLink account={account} size={AVATAR_SIZE} />;
|
||||
};
|
||||
|
||||
export type LabelRenderer = (
|
||||
@@ -108,7 +110,7 @@ export const NotificationGroupWithStatus: React.FC<{
|
||||
<div className='notification-group__main'>
|
||||
<div className='notification-group__main__header'>
|
||||
<div className='notification-group__main__header__wrapper'>
|
||||
<AvatarGroup>
|
||||
<AvatarGroup avatarHeight={AVATAR_SIZE}>
|
||||
{accountIds
|
||||
.slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS)
|
||||
.map((id) => (
|
||||
@@ -123,7 +125,14 @@ export const NotificationGroupWithStatus: React.FC<{
|
||||
|
||||
<div className='notification-group__main__header__label'>
|
||||
{label}
|
||||
{timestamp && <RelativeTimestamp timestamp={timestamp} />}
|
||||
{timestamp && (
|
||||
<>
|
||||
<span className='notification-group__main__header__label-separator'>
|
||||
·
|
||||
</span>
|
||||
<RelativeTimestamp timestamp={timestamp} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from 'mastodon/actions/statuses';
|
||||
import type { IconProp } from 'mastodon/components/icon';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import Status from 'mastodon/containers/status_container';
|
||||
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
||||
import { getStatusHidden } from 'mastodon/selectors/filters';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
|
||||
@@ -102,8 +102,7 @@ export const NotificationWithStatus: React.FC<{
|
||||
{label}
|
||||
</div>
|
||||
|
||||
<Status
|
||||
// @ts-expect-error -- <Status> is not yet typed
|
||||
<StatusQuoteManager
|
||||
id={statusId}
|
||||
contextType='notifications'
|
||||
withDismiss
|
||||
|
||||
@@ -51,7 +51,7 @@ class Rules extends PureComponent {
|
||||
value={item.get('id')}
|
||||
checked={selectedRuleIds.includes(item.get('id'))}
|
||||
onToggle={this.handleRulesToggle}
|
||||
label={item.getIn(['translations', locale, 'text']) || item.get('text')}
|
||||
label={item.getIn(['translations', locale, 'text']) || item.getIn(['translations', locale.split('-')[0], 'text']) || item.get('text')}
|
||||
multiple
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ColumnHeader } from 'mastodon/components/column_header';
|
||||
import { CompatibilityHashtag as Hashtag } from 'mastodon/components/hashtag';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||
import Status from 'mastodon/containers/status_container';
|
||||
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
||||
import { Search } from 'mastodon/features/compose/components/search';
|
||||
import { useSearchParam } from 'mastodon/hooks/useSearchParam';
|
||||
import type { Hashtag as HashtagType } from 'mastodon/models/tags';
|
||||
@@ -53,8 +53,7 @@ const renderHashtags = (hashtags: HashtagType[]) =>
|
||||
|
||||
const renderStatuses = (statusIds: string[]) =>
|
||||
hidePeek<string>(statusIds).map((id) => (
|
||||
// @ts-expect-error inferred props are wrong
|
||||
<Status key={id} id={id} />
|
||||
<StatusQuoteManager key={id} id={id} />
|
||||
));
|
||||
|
||||
type SearchType = 'all' | ApiSearchType;
|
||||
@@ -190,8 +189,7 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
|
||||
onClickMore={handleSelectStatuses}
|
||||
>
|
||||
{results.statuses.slice(0, INITIAL_DISPLAY).map((id) => (
|
||||
// @ts-expect-error inferred props are wrong
|
||||
<Status key={id} id={id} />
|
||||
<StatusQuoteManager key={id} id={id} />
|
||||
))}
|
||||
</SearchSection>
|
||||
)}
|
||||
|
||||
@@ -26,6 +26,7 @@ import { IconLogo } from 'mastodon/components/logo';
|
||||
import MediaGallery from 'mastodon/components/media_gallery';
|
||||
import { PictureInPicturePlaceholder } from 'mastodon/components/picture_in_picture_placeholder';
|
||||
import StatusContent from 'mastodon/components/status_content';
|
||||
import { QuotedStatus } from 'mastodon/components/status_quoted';
|
||||
import { VisibilityIcon } from 'mastodon/components/visibility_icon';
|
||||
import { Audio } from 'mastodon/features/audio';
|
||||
import scheduleIdleTask from 'mastodon/features/ui/util/schedule_idle_task';
|
||||
@@ -226,7 +227,7 @@ export const DetailedStatus: React.FC<{
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (status.get('card')) {
|
||||
} else if (status.get('card') && !status.get('quote')) {
|
||||
media = (
|
||||
<Card
|
||||
sensitive={status.get('sensitive')}
|
||||
@@ -306,7 +307,12 @@ export const DetailedStatus: React.FC<{
|
||||
|
||||
return (
|
||||
<div style={outerStyle}>
|
||||
<div ref={handleRef} className={classNames('detailed-status')}>
|
||||
<div
|
||||
ref={handleRef}
|
||||
className={classNames('detailed-status', {
|
||||
'status--has-quote': !!status.get('quote'),
|
||||
})}
|
||||
>
|
||||
{status.get('visibility') === 'direct' && (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'>
|
||||
@@ -371,6 +377,10 @@ export const DetailedStatus: React.FC<{
|
||||
{...(statusContentProps as any)}
|
||||
/>
|
||||
|
||||
{status.get('quote') && (
|
||||
<QuotedStatus quote={status.get('quote')} />
|
||||
)}
|
||||
|
||||
{media}
|
||||
{hashtagBar}
|
||||
</>
|
||||
|
||||
@@ -6,8 +6,6 @@ import classNames from 'classnames';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -62,7 +60,7 @@ import {
|
||||
} from '../../actions/statuses';
|
||||
import ColumnHeader from '../../components/column_header';
|
||||
import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
|
||||
import StatusContainer from '../../containers/status_container';
|
||||
import { StatusQuoteManager } from '../../components/status_quoted';
|
||||
import { deleteModal } from '../../initial_state';
|
||||
import { makeGetStatus, makeGetPictureInPicture } from '../../selectors';
|
||||
import { getAncestorsIds, getDescendantsIds } from 'mastodon/selectors/contexts';
|
||||
@@ -477,7 +475,7 @@ class Status extends ImmutablePureComponent {
|
||||
const { params: { statusId } } = this.props;
|
||||
|
||||
return list.map((id, i) => (
|
||||
<StatusContainer
|
||||
<StatusQuoteManager
|
||||
key={id}
|
||||
id={id}
|
||||
onMoveUp={this.handleMoveUp}
|
||||
|
||||
@@ -24,9 +24,7 @@ export const HotkeyIndicator: React.FC<{
|
||||
enter: [{ opacity: 1 }],
|
||||
leave: [{ opacity: 0 }],
|
||||
onRest: (_result, _ctrl, item) => {
|
||||
if (item) {
|
||||
onDismiss(item);
|
||||
}
|
||||
onDismiss(item);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -23,9 +23,11 @@
|
||||
"account.copy": "Profil linkini kopyala",
|
||||
"account.direct": "@{name} istifadəçisini fərdi olaraq etiketlə",
|
||||
"account.disable_notifications": "@{name} paylaşım edəndə mənə bildiriş göndərməyi dayandır",
|
||||
"account.domain_blocking": "Domenin bloklanması",
|
||||
"account.edit_profile": "Profili redaktə et",
|
||||
"account.enable_notifications": "@{name} paylaşım edəndə mənə bildiriş göndər",
|
||||
"account.endorse": "Profildə seçilmişlərə əlavə et",
|
||||
"account.featured.hashtags": "Etiketler",
|
||||
"account.featured_tags.last_status_at": "Son paylaşım {date} tarixində olub",
|
||||
"account.featured_tags.last_status_never": "Paylaşım yoxdur",
|
||||
"account.follow": "İzlə",
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"account.followers": "Tud koumanantet",
|
||||
"account.followers.empty": "Den na heul an implijer·ez-mañ c'hoazh.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} heulier} two {{counter} heulier} few {{counter} heulier} many {{counter} heulier} other {{counter} heulier}}",
|
||||
"account.followers_you_know_counter": "{counter} a anavezit",
|
||||
"account.following": "Koumanantoù",
|
||||
"account.follows.empty": "An implijer·ez-mañ na heul den ebet.",
|
||||
"account.go_to_profile": "Gwelet ar profil",
|
||||
@@ -577,6 +578,7 @@
|
||||
"status.mute": "Kuzhat @{name}",
|
||||
"status.mute_conversation": "Kuzhat ar gaozeadenn",
|
||||
"status.open": "Digeriñ ar c'hannad-mañ",
|
||||
"status.quote_post_author": "Embannadenn gant {name}",
|
||||
"status.read_more": "Lenn muioc'h",
|
||||
"status.reblog": "Skignañ",
|
||||
"status.reblog_private": "Skignañ gant ar weledenn gentañ",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Sledující",
|
||||
"account.followers.empty": "Tohoto uživatele zatím nikdo nesleduje.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} sledující} few {{counter} sledující} many {{counter} sledujících} other {{counter} sledujících}}",
|
||||
"account.followers_you_know_counter": "{count, one {{counter}, kterého znáte}, few {{counter}, které znáte}, many {{counter}, kterých znáte} other {{counter}, kterých znáte}}",
|
||||
"account.following": "Sledujete",
|
||||
"account.following_counter": "{count, plural, one {{counter} sledovaný} few {{counter} sledovaní} many {{counter} sledovaných} other {{counter} sledovaných}}",
|
||||
"account.follows.empty": "Tento uživatel zatím nikoho nesleduje.",
|
||||
@@ -683,7 +684,7 @@
|
||||
"notifications.policy.filter_not_followers_title": "Lidé, kteří vás nesledují",
|
||||
"notifications.policy.filter_not_following_hint": "Dokud je ručně neschválíte",
|
||||
"notifications.policy.filter_not_following_title": "Lidé, které nesledujete",
|
||||
"notifications.policy.filter_private_mentions_hint": "Vyfiltrováno, pokud to není odpověď na vaši zmínku nebo pokud sledujete odesílatele",
|
||||
"notifications.policy.filter_private_mentions_hint": "Filtrováno, pokud to není v odpovědi na vaši vlastní zmínku nebo pokud nesledujete odesílatele",
|
||||
"notifications.policy.filter_private_mentions_title": "Nevyžádané soukromé zmínky",
|
||||
"notifications.policy.title": "Spravovat oznámení od…",
|
||||
"notifications_permission_banner.enable": "Povolit oznámení na ploše",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Skrýt konverzaci",
|
||||
"status.open": "Rozbalit tento příspěvek",
|
||||
"status.pin": "Zvýraznit na profilu",
|
||||
"status.quote_error.not_found": "Tento příspěvek nelze zobrazit.",
|
||||
"status.quote_error.pending_approval": "Tento příspěvek čeká na schválení od původního autora.",
|
||||
"status.quote_error.rejected": "Tento příspěvek nemůže být zobrazen, protože původní autor neumožňuje, aby byl citován.",
|
||||
"status.quote_error.removed": "Tento příspěvek byl odstraněn jeho autorem.",
|
||||
"status.quote_error.unauthorized": "Tento příspěvek nelze zobrazit, protože nemáte oprávnění k jeho zobrazení.",
|
||||
"status.quote_post_author": "Příspěvek od {name}",
|
||||
"status.read_more": "Číst více",
|
||||
"status.reblog": "Boostnout",
|
||||
"status.reblog_private": "Boostnout s původní viditelností",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Dilynwyr",
|
||||
"account.followers.empty": "Does neb yn dilyn y defnyddiwr hwn eto.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} dilynwr} two {{counter} ddilynwr} other {{counter} dilynwyr}}",
|
||||
"account.followers_you_know_counter": "{counter} rydych chi'n adnabod",
|
||||
"account.following": "Yn dilyn",
|
||||
"account.following_counter": "{count, plural, one {Yn dilyn {counter}} other {Yn dilyn {counter} arall}}",
|
||||
"account.follows.empty": "Dyw'r defnyddiwr hwn ddim yn dilyn unrhyw un eto.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Anwybyddu sgwrs",
|
||||
"status.open": "Ehangu'r post hwn",
|
||||
"status.pin": "Dangos ar y proffil",
|
||||
"status.quote_error.not_found": "Does dim modd dangos y postiad hwn.",
|
||||
"status.quote_error.pending_approval": "Mae'r postiad hwn yn aros am gymeradwyaeth yr awdur gwreiddiol.",
|
||||
"status.quote_error.rejected": "Does dim modd dangos y postiad hwn gan nad yw'r awdur gwreiddiol yn caniatáu iddo gael ei ddyfynnu.",
|
||||
"status.quote_error.removed": "Cafodd y postiad hwn ei ddileu gan ei awdur.",
|
||||
"status.quote_error.unauthorized": "Does dim modd dangos y postiad hwn gan nad oes gennych awdurdod i'w weld.",
|
||||
"status.quote_post_author": "Postiad gan {name}",
|
||||
"status.read_more": "Darllen rhagor",
|
||||
"status.reblog": "Hybu",
|
||||
"status.reblog_private": "Hybu i'r gynulleidfa wreiddiol",
|
||||
|
||||
@@ -863,6 +863,12 @@
|
||||
"status.mute_conversation": "Skjul samtale",
|
||||
"status.open": "Udvid dette indlæg",
|
||||
"status.pin": "Fremhæv på profil",
|
||||
"status.quote_error.not_found": "Dette indlæg kan ikke vises.",
|
||||
"status.quote_error.pending_approval": "Dette indlæg afventer godkendelse fra den oprindelige forfatter.",
|
||||
"status.quote_error.rejected": "Dette indlæg kan ikke vises, da den oprindelige forfatter ikke tillader citering heraf.",
|
||||
"status.quote_error.removed": "Dette indlæg er fjernet af forfatteren.",
|
||||
"status.quote_error.unauthorized": "Dette indlæg kan ikke vises, da man ikke har tilladelse til at se det.",
|
||||
"status.quote_post_author": "Indlæg fra {name}",
|
||||
"status.read_more": "Læs mere",
|
||||
"status.reblog": "Fremhæv",
|
||||
"status.reblog_private": "Fremhæv med oprindelig synlighed",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Follower",
|
||||
"account.followers.empty": "Diesem Profil folgt noch niemand.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}",
|
||||
"account.followers_you_know_counter": "{counter} bekannt",
|
||||
"account.following": "Folge ich",
|
||||
"account.following_counter": "{count, plural, one {{counter} Folge ich} other {{counter} Folge ich}}",
|
||||
"account.follows.empty": "Dieses Profil folgt noch niemandem.",
|
||||
@@ -360,7 +361,7 @@
|
||||
"filter_modal.select_filter.title": "Diesen Beitrag filtern",
|
||||
"filter_modal.title.status": "Beitrag per Filter ausblenden",
|
||||
"filter_warning.matches_filter": "Übereinstimmend mit dem Filter „<span>{title}</span>“",
|
||||
"filtered_notifications_banner.pending_requests": "Von {count, plural, =0 {keinem, den} one {einer Person, die} other {# Personen, die}} du möglicherweise kennst",
|
||||
"filtered_notifications_banner.pending_requests": "Von {count, plural, =0 {keinem Profil, das dir möglicherweise bekannt ist} one {einem Profil, das dir möglicherweise bekannt ist} other {# Profilen, die dir möglicherweise bekannt sind}}",
|
||||
"filtered_notifications_banner.title": "Gefilterte Benachrichtigungen",
|
||||
"firehose.all": "Alle Server",
|
||||
"firehose.local": "Dieser Server",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Unterhaltung stummschalten",
|
||||
"status.open": "Beitrag öffnen",
|
||||
"status.pin": "Im Profil vorstellen",
|
||||
"status.quote_error.not_found": "Dieser Beitrag kann nicht angezeigt werden.",
|
||||
"status.quote_error.pending_approval": "Dieser Beitrag muss noch durch das ursprüngliche Profil genehmigt werden.",
|
||||
"status.quote_error.rejected": "Dieser Beitrag kann nicht angezeigt werden, weil das ursprüngliche Profil das Zitieren nicht erlaubt.",
|
||||
"status.quote_error.removed": "Dieser Beitrag wurde durch das Profil entfernt.",
|
||||
"status.quote_error.unauthorized": "Dieser Beitrag kann nicht angezeigt werden, weil du zum Ansehen nicht berechtigt bist.",
|
||||
"status.quote_post_author": "Beitrag von {name}",
|
||||
"status.read_more": "Gesamten Beitrag anschauen",
|
||||
"status.reblog": "Teilen",
|
||||
"status.reblog_private": "Mit der ursprünglichen Zielgruppe teilen",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Followers",
|
||||
"account.followers.empty": "No one follows this user yet.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} follower} other {{counter} followers}}",
|
||||
"account.followers_you_know_counter": "{counter} you know",
|
||||
"account.following": "Following",
|
||||
"account.following_counter": "{count, plural, one {{counter} following} other {{counter} following}}",
|
||||
"account.follows.empty": "This user doesn't follow anyone yet.",
|
||||
@@ -863,6 +864,13 @@
|
||||
"status.mute_conversation": "Mute conversation",
|
||||
"status.open": "Expand this post",
|
||||
"status.pin": "Feature on profile",
|
||||
"status.quote_error.filtered": "Hidden due to one of your filters",
|
||||
"status.quote_error.not_found": "This post cannot be displayed.",
|
||||
"status.quote_error.pending_approval": "This post is pending approval from the original author.",
|
||||
"status.quote_error.rejected": "This post cannot be displayed as the original author does not allow it to be quoted.",
|
||||
"status.quote_error.removed": "This post was removed by its author.",
|
||||
"status.quote_error.unauthorized": "This post cannot be displayed as you are not authorized to view it.",
|
||||
"status.quote_post_author": "Post by {name}",
|
||||
"status.read_more": "Read more",
|
||||
"status.reblog": "Boost",
|
||||
"status.reblog_private": "Boost with original visibility",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Seguidores",
|
||||
"account.followers.empty": "Todavía nadie sigue a este usuario.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
|
||||
"account.followers_you_know_counter": "{counter} seguidores que conocés",
|
||||
"account.following": "Siguiendo",
|
||||
"account.following_counter": "{count, plural, one {siguiendo a {counter}} other {siguiendo a {counter}}}",
|
||||
"account.follows.empty": "Todavía este usuario no sigue a nadie.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Silenciar conversación",
|
||||
"status.open": "Expandir este mensaje",
|
||||
"status.pin": "Destacar en el perfil",
|
||||
"status.quote_error.not_found": "No se puede mostrar este mensaje.",
|
||||
"status.quote_error.pending_approval": "Este mensaje está pendiente de aprobación del autor original.",
|
||||
"status.quote_error.rejected": "No se puede mostrar este mensaje, ya que el autor original no permite que se cite.",
|
||||
"status.quote_error.removed": "Este mensaje fue eliminado por su autor.",
|
||||
"status.quote_error.unauthorized": "No se puede mostrar este mensaje, ya que no tenés autorización para verlo.",
|
||||
"status.quote_post_author": "Mensaje de @{name}",
|
||||
"status.read_more": "Leé más",
|
||||
"status.reblog": "Adherir",
|
||||
"status.reblog_private": "Adherir a la audiencia original",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Seguidores",
|
||||
"account.followers.empty": "Nadie sigue a este usuario todavía.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
|
||||
"account.followers_you_know_counter": "{counter} que conoces",
|
||||
"account.following": "Siguiendo",
|
||||
"account.following_counter": "{count, plural, one {{counter} siguiendo} other {{counter} siguiendo}}",
|
||||
"account.follows.empty": "Este usuario no sigue a nadie todavía.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Silenciar conversación",
|
||||
"status.open": "Expandir estado",
|
||||
"status.pin": "Destacar en el perfil",
|
||||
"status.quote_error.not_found": "No se puede mostrar esta publicación.",
|
||||
"status.quote_error.pending_approval": "Esta publicación está pendiente de aprobación del autor original.",
|
||||
"status.quote_error.rejected": "No se puede mostrar esta publicación, puesto que el autor original no permite que sea citado.",
|
||||
"status.quote_error.removed": "Esta publicación fue eliminada por su autor.",
|
||||
"status.quote_error.unauthorized": "No se puede mostrar esta publicación, puesto que no estás autorizado a verla.",
|
||||
"status.quote_post_author": "Publicado por {name}",
|
||||
"status.read_more": "Leer más",
|
||||
"status.reblog": "Impulsar",
|
||||
"status.reblog_private": "Implusar a la audiencia original",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Seguidores",
|
||||
"account.followers.empty": "Todavía nadie sigue a este usuario.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
|
||||
"account.followers_you_know_counter": "{counter} seguidores que conoces",
|
||||
"account.following": "Siguiendo",
|
||||
"account.following_counter": "{count, plural, one {{counter} siguiendo} other {{counter} siguiendo}}",
|
||||
"account.follows.empty": "Este usuario todavía no sigue a nadie.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Silenciar conversación",
|
||||
"status.open": "Expandir publicación",
|
||||
"status.pin": "Destacar en el perfil",
|
||||
"status.quote_error.not_found": "No se puede mostrar esta publicación.",
|
||||
"status.quote_error.pending_approval": "Esta publicación está pendiente de aprobación del autor original.",
|
||||
"status.quote_error.rejected": "Esta publicación no puede mostrarse porque el autor original no permite que se cite.",
|
||||
"status.quote_error.removed": "Esta publicación fue eliminada por su autor.",
|
||||
"status.quote_error.unauthorized": "Esta publicación no puede mostrarse, ya que no estás autorizado a verla.",
|
||||
"status.quote_post_author": "Publicación de {name}",
|
||||
"status.read_more": "Leer más",
|
||||
"status.reblog": "Impulsar",
|
||||
"status.reblog_private": "Impulsar a la audiencia original",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Seuraajat",
|
||||
"account.followers.empty": "Kukaan ei seuraa tätä käyttäjää vielä.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seuraaja} other {{counter} seuraajaa}}",
|
||||
"account.followers_you_know_counter": "{count, plural, one {{counter} tuntemasi} other {{counter} tuntemaasi}}",
|
||||
"account.following": "Seurattavat",
|
||||
"account.following_counter": "{count, plural, one {{counter} seurattava} other {{counter} seurattavaa}}",
|
||||
"account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Mykistä keskustelu",
|
||||
"status.open": "Laajenna julkaisu",
|
||||
"status.pin": "Suosittele profiilissa",
|
||||
"status.quote_error.not_found": "Tätä julkaisua ei voi näyttää.",
|
||||
"status.quote_error.pending_approval": "Tämä julkaisu odottaa alkuperäisen tekijänsä hyväksyntää.",
|
||||
"status.quote_error.rejected": "Tätä julkaisua ei voi näyttää, sillä sen alkuperäinen tekijä ei salli lainattavan julkaisua.",
|
||||
"status.quote_error.removed": "Tekijä on poistanut julkaisun.",
|
||||
"status.quote_error.unauthorized": "Tätä julkaisua ei voi näyttää, koska sinulla ei ole oikeutta tarkastella sitä.",
|
||||
"status.quote_post_author": "Julkaisu käyttäjältä {name}",
|
||||
"status.read_more": "Näytä enemmän",
|
||||
"status.reblog": "Tehosta",
|
||||
"status.reblog_private": "Tehosta alkuperäiselle yleisölle",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Fylgjarar",
|
||||
"account.followers.empty": "Ongar fylgjarar enn.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} fylgjari} other {{counter} fylgjarar}}",
|
||||
"account.followers_you_know_counter": "{counter} tú kennir",
|
||||
"account.following": "Fylgir",
|
||||
"account.following_counter": "{count, plural, one {{counter} fylgir} other {{counter} fylgja}}",
|
||||
"account.follows.empty": "Hesin brúkari fylgir ongum enn.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Doyv samrøðu",
|
||||
"status.open": "Víðka henda postin",
|
||||
"status.pin": "Vís á vanga",
|
||||
"status.quote_error.not_found": "Tað ber ikki til at vísa hendan postin.",
|
||||
"status.quote_error.pending_approval": "Hesin posturin bíðar eftir góðkenning frá upprunahøvundinum.",
|
||||
"status.quote_error.rejected": "Hesin posturin kann ikki vísast, tí upprunahøvundurin loyvir ikki at posturin verður siteraður.",
|
||||
"status.quote_error.removed": "Hesin posturin var strikaður av høvundinum.",
|
||||
"status.quote_error.unauthorized": "Hesin posturin kann ikki vísast, tí tú hevur ikki rættindi at síggja hann.",
|
||||
"status.quote_post_author": "Postur hjá @{name}",
|
||||
"status.read_more": "Les meira",
|
||||
"status.reblog": "Stimbra",
|
||||
"status.reblog_private": "Stimbra við upprunasýni",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Seguidoras",
|
||||
"account.followers.empty": "Aínda ninguén segue esta usuaria.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seguidora} other {{counter} seguidoras}}",
|
||||
"account.followers_you_know_counter": "{counter} que coñeces",
|
||||
"account.following": "Seguindo",
|
||||
"account.following_counter": "{count, plural, one {{counter} seguimento} other {{counter} seguimentos}}",
|
||||
"account.follows.empty": "Esta usuaria aínda non segue a ninguén.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Silenciar conversa",
|
||||
"status.open": "Estender esta publicación",
|
||||
"status.pin": "Destacar no perfil",
|
||||
"status.quote_error.not_found": "Non se pode mostrar a publicación.",
|
||||
"status.quote_error.pending_approval": "A publicación está pendente da aprobación pola autora orixinal.",
|
||||
"status.quote_error.rejected": "Non se pode mostrar esta publicación xa que a autora orixinal non permite que se cite.",
|
||||
"status.quote_error.removed": "Publicación eliminada pola autora.",
|
||||
"status.quote_error.unauthorized": "Non se pode mostrar esta publicación porque non tes permiso para vela.",
|
||||
"status.quote_post_author": "Publicación de {name}",
|
||||
"status.read_more": "Ler máis",
|
||||
"status.reblog": "Promover",
|
||||
"status.reblog_private": "Compartir coa audiencia orixinal",
|
||||
|
||||
@@ -863,6 +863,12 @@
|
||||
"status.mute_conversation": "השתקת שיחה",
|
||||
"status.open": "הרחבת הודעה זו",
|
||||
"status.pin": "מובלט בפרופיל",
|
||||
"status.quote_error.not_found": "לא ניתן להציג הודעה זו.",
|
||||
"status.quote_error.pending_approval": "הודעה זו מחכה לאישור מידי היוצר המקורי.",
|
||||
"status.quote_error.rejected": "לא ניתן להציג הודעה זו שכן המחבר.ת המקוריים לא הרשו לצטט אותה.",
|
||||
"status.quote_error.removed": "הודעה זו הוסרה על ידי השולחים המקוריים.",
|
||||
"status.quote_error.unauthorized": "הודעה זו לא מוצגת כיוון שאין לך רשות לראותה.",
|
||||
"status.quote_post_author": "פרסום מאת {name}",
|
||||
"status.read_more": "לקרוא עוד",
|
||||
"status.reblog": "הדהוד",
|
||||
"status.reblog_private": "להדהד ברמת הנראות המקורית",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Követő",
|
||||
"account.followers.empty": "Ezt a felhasználót még senki sem követi.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} követő} other {{counter} követő}}",
|
||||
"account.followers_you_know_counter": "{counter} ismerős",
|
||||
"account.following": "Követve",
|
||||
"account.following_counter": "{count, plural, one {{counter} követett} other {{counter} követett}}",
|
||||
"account.follows.empty": "Ez a felhasználó még senkit sem követ.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Beszélgetés némítása",
|
||||
"status.open": "Bejegyzés kibontása",
|
||||
"status.pin": "Kiemelés a profilodon",
|
||||
"status.quote_error.not_found": "Ez a bejegyzés nem jeleníthető meg.",
|
||||
"status.quote_error.pending_approval": "Ez a bejegyzés az eredeti szerző jóváhagyására vár.",
|
||||
"status.quote_error.rejected": "Ez a bejegyzés nem jeleníthető meg, mert az eredeti szerzője nem engedélyezi az idézését.",
|
||||
"status.quote_error.removed": "Ezt a bejegyzés eltávolította a szerzője.",
|
||||
"status.quote_error.unauthorized": "Ez a bejegyzés nem jeleníthető meg, mert nem jogosult a megtekintésére.",
|
||||
"status.quote_post_author": "Szerző: {name}",
|
||||
"status.read_more": "Bővebben",
|
||||
"status.reblog": "Megtolás",
|
||||
"status.reblog_private": "Megtolás az eredeti közönségnek",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Fylgjendur",
|
||||
"account.followers.empty": "Ennþá fylgist enginn með þessum notanda.",
|
||||
"account.followers_counter": "{count, plural, one {Fylgjandi: {counter}} other {Fylgjendur: {counter}}}",
|
||||
"account.followers_you_know_counter": "{counter} sem þú þekkir",
|
||||
"account.following": "Fylgist með",
|
||||
"account.following_counter": "{count, plural, one {Fylgist með: {counter}} other {Fylgist með: {counter}}}",
|
||||
"account.follows.empty": "Þessi notandi fylgist ennþá ekki með neinum.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Þagga niður í samtali",
|
||||
"status.open": "Opna þessa færslu",
|
||||
"status.pin": "Birta á notandasniði",
|
||||
"status.quote_error.not_found": "Þessa færslu er ekki hægt að birta.",
|
||||
"status.quote_error.pending_approval": "Þessi færsla bíður eftir samþykki frá upprunalegum höfundi hennar.",
|
||||
"status.quote_error.rejected": "Þessa færslu er ekki hægt að birta þar sem upphaflegur höfundur hennar leyfir ekki að vitnað sé til hennar.",
|
||||
"status.quote_error.removed": "Þessi færsla var fjarlægð af höfundi hennar.",
|
||||
"status.quote_error.unauthorized": "Þessa færslu er ekki hægt að birta þar sem þú hefur ekki heimild til að skoða hana.",
|
||||
"status.quote_post_author": "Færsla frá {name}",
|
||||
"status.read_more": "Lesa meira",
|
||||
"status.reblog": "Endurbirting",
|
||||
"status.reblog_private": "Endurbirta til upphaflegra lesenda",
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
"account.edit_profile": "프로필 편집",
|
||||
"account.enable_notifications": "@{name} 의 게시물 알림 켜기",
|
||||
"account.endorse": "프로필에 추천하기",
|
||||
"account.familiar_followers_many": "{name1}, {name2} 님 외 내가 아는 {othersCount, plural, other {#}} 명이 팔로우함",
|
||||
"account.familiar_followers_one": "{name1} 님이 팔로우함",
|
||||
"account.familiar_followers_two": "{name1}, {name2} 님이 팔로우함",
|
||||
"account.featured": "추천",
|
||||
"account.featured.accounts": "프로필",
|
||||
"account.featured.hashtags": "해시태그",
|
||||
@@ -406,8 +409,10 @@
|
||||
"hashtag.counter_by_accounts": "{count, plural, other {참여자 {counter}명}}",
|
||||
"hashtag.counter_by_uses": "{count, plural, other {게시물 {counter}개}}",
|
||||
"hashtag.counter_by_uses_today": "오늘 {count, plural, other {{counter} 개의 게시물}}",
|
||||
"hashtag.feature": "프로필에 추천하기",
|
||||
"hashtag.follow": "해시태그 팔로우",
|
||||
"hashtag.mute": "#{hashtag} 뮤트",
|
||||
"hashtag.unfeature": "프로필에 추천하지 않기",
|
||||
"hashtag.unfollow": "해시태그 팔로우 해제",
|
||||
"hashtags.and_other": "…및 {count, plural,other {#개}}",
|
||||
"hints.profiles.followers_may_be_missing": "이 프로필의 팔로워 목록은 일부 누락되었을 수 있습니다.",
|
||||
@@ -858,6 +863,11 @@
|
||||
"status.mute_conversation": "대화 뮤트",
|
||||
"status.open": "상세 정보 표시",
|
||||
"status.pin": "고정",
|
||||
"status.quote_error.not_found": "이 게시물은 표시할 수 없습니다.",
|
||||
"status.quote_error.pending_approval": "이 게시물은 원작자의 승인을 기다리고 있습니다.",
|
||||
"status.quote_error.rejected": "이 게시물은 원작자가 인용을 허용하지 않았기 때문에 표시할 수 없습니다.",
|
||||
"status.quote_error.removed": "이 게시물은 작성자에 의해 삭제되었습니다.",
|
||||
"status.quote_error.unauthorized": "이 게시물은 권한이 없기 때문에 볼 수 없습니다.",
|
||||
"status.read_more": "더 보기",
|
||||
"status.reblog": "부스트",
|
||||
"status.reblog_private": "원래의 수신자들에게 부스트",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"account.followers": "Sekotāji",
|
||||
"account.followers.empty": "Šim lietotājam vēl nav sekotāju.",
|
||||
"account.followers_counter": "{count, plural, zero {{count} sekotāju} one {{count} sekotājs} other {{count} sekotāji}}",
|
||||
"account.followers_you_know_counter": "{counter} jūs pazīstiet",
|
||||
"account.following": "Seko",
|
||||
"account.following_counter": "{count, plural, one {seko {counter}} other {seko {counter}}}",
|
||||
"account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.",
|
||||
@@ -75,6 +76,7 @@
|
||||
"account.statuses_counter": "{count, plural, zero {{counter} ierakstu} one {{counter} ieraksts} other {{counter} ieraksti}}",
|
||||
"account.unblock": "Atbloķēt @{name}",
|
||||
"account.unblock_domain": "Atbloķēt domēnu {domain}",
|
||||
"account.unblock_domain_short": "Atbloķēt",
|
||||
"account.unblock_short": "Atbloķēt",
|
||||
"account.unendorse": "Neizcelt profilā",
|
||||
"account.unfollow": "Pārstāt sekot",
|
||||
@@ -367,6 +369,8 @@
|
||||
"generic.saved": "Saglabāts",
|
||||
"getting_started.heading": "Darba sākšana",
|
||||
"hashtag.admin_moderation": "Atvērt #{name} satura pārraudzības saskarni",
|
||||
"hashtag.browse": "Pārlūkot #{hashtag} ierakstus",
|
||||
"hashtag.browse_from_account": "Pārlūkot @{name} #{hashtag} ierakstus",
|
||||
"hashtag.column_header.tag_mode.all": "un {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "vai {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "bez {additional}",
|
||||
@@ -381,6 +385,7 @@
|
||||
"hashtag.counter_by_uses_today": "{count, plural, zero {{counter} ierakstu} one {{counter} ieraksts} other {{counter} ieraksti}} šodien",
|
||||
"hashtag.feature": "Attēlot profilā",
|
||||
"hashtag.follow": "Sekot tēmturim",
|
||||
"hashtag.mute": "Apklusināt #{hashtag}",
|
||||
"hashtag.unfeature": "Neattēlot profilā",
|
||||
"hashtag.unfollow": "Pārstāt sekot tēmturim",
|
||||
"hashtags.and_other": "… un {count, plural, other {vēl #}}",
|
||||
@@ -448,6 +453,7 @@
|
||||
"keyboard_shortcuts.toggle_hidden": "Rādīt/slēpt tekstu aiz satura brīdinājuma",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Rādīt/slēpt multividi",
|
||||
"keyboard_shortcuts.toot": "Uzsākt jaunu ierakstu",
|
||||
"keyboard_shortcuts.translate": "tulkot ierakstu",
|
||||
"keyboard_shortcuts.unfocus": "Atfokusēt veidojamā teksta/meklēšanas lauku",
|
||||
"keyboard_shortcuts.up": "Pārvietoties augšup sarakstā",
|
||||
"lightbox.close": "Aizvērt",
|
||||
@@ -515,6 +521,7 @@
|
||||
"notification.favourite": "{name} pievienoja izlasei Tavu ierakstu",
|
||||
"notification.follow": "{name} uzsāka Tev sekot",
|
||||
"notification.follow_request": "{name} nosūtīja Tev sekošanas pieprasījumu",
|
||||
"notification.mentioned_you": "{name} pieminēja jūs",
|
||||
"notification.moderation-warning.learn_more": "Uzzināt vairāk",
|
||||
"notification.moderation_warning": "Ir saņemts satura pārraudzības brīdinājums",
|
||||
"notification.moderation_warning.action_delete_statuses": "Daži no Taviem ierakstiem tika noņemti.",
|
||||
@@ -741,6 +748,12 @@
|
||||
"status.mute_conversation": "Apklusināt sarunu",
|
||||
"status.open": "Izvērst šo ierakstu",
|
||||
"status.pin": "Attēlot profilā",
|
||||
"status.quote_error.not_found": "Šo ierakstu nevar parādīt.",
|
||||
"status.quote_error.pending_approval": "Šis ieraksts gaida apstiprinājumu no tā autora.",
|
||||
"status.quote_error.rejected": "Šo ierakstu nevar parādīt, jo tā autors neļauj to citēt.",
|
||||
"status.quote_error.removed": "Šo ierakstu noņēma tā autors.",
|
||||
"status.quote_error.unauthorized": "Šo ierakstu nevar parādīt, jo jums nav atļaujas to skatīt.",
|
||||
"status.quote_post_author": "Publicēja {name}",
|
||||
"status.read_more": "Lasīt vairāk",
|
||||
"status.reblog": "Pastiprināt",
|
||||
"status.reblog_private": "Pastiprināt ar sākotnējo redzamību",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Volgers",
|
||||
"account.followers.empty": "Deze gebruiker heeft nog geen volgers of heeft deze verborgen.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} volger} other {{counter} volgers}}",
|
||||
"account.followers_you_know_counter": "{counter} die je kent",
|
||||
"account.following": "Volgend",
|
||||
"account.following_counter": "{count, plural, one {{counter} volgend} other {{counter} volgend}}",
|
||||
"account.follows.empty": "Deze gebruiker volgt nog niemand of heeft deze verborgen.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Gesprek negeren",
|
||||
"status.open": "Volledig bericht tonen",
|
||||
"status.pin": "Op profiel uitlichten",
|
||||
"status.quote_error.not_found": "Dit bericht kan niet worden weergegeven.",
|
||||
"status.quote_error.pending_approval": "Dit bericht is in afwachting van goedkeuring door de oorspronkelijke auteur.",
|
||||
"status.quote_error.rejected": "Dit bericht kan niet worden weergegeven omdat de oorspronkelijke auteur niet toestaat dat het wordt geciteerd.",
|
||||
"status.quote_error.removed": "Dit bericht is verwijderd door de auteur.",
|
||||
"status.quote_error.unauthorized": "Dit bericht kan niet worden weergegeven omdat je niet bevoegd bent om het te bekijken.",
|
||||
"status.quote_post_author": "Bericht van {name}",
|
||||
"status.read_more": "Meer lezen",
|
||||
"status.reblog": "Boosten",
|
||||
"status.reblog_private": "Boost naar oorspronkelijke ontvangers",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Seguidores",
|
||||
"account.followers.empty": "Ainda ninguém segue este utilizador.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
|
||||
"account.followers_you_know_counter": "{counter} que conhece",
|
||||
"account.following": "A seguir",
|
||||
"account.following_counter": "{count, plural, one {A seguir {counter}} other {A seguir {counter}}}",
|
||||
"account.follows.empty": "Este utilizador ainda não segue ninguém.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Ocultar conversa",
|
||||
"status.open": "Expandir esta publicação",
|
||||
"status.pin": "Destacar no perfil",
|
||||
"status.quote_error.not_found": "Esta publicação não pode ser exibida.",
|
||||
"status.quote_error.pending_approval": "Esta publicação está a aguardar a aprovação do autor original.",
|
||||
"status.quote_error.rejected": "Esta publicação não pode ser exibida porque o autor original não permite que seja citada.",
|
||||
"status.quote_error.removed": "Esta publicação foi removida pelo seu autor.",
|
||||
"status.quote_error.unauthorized": "Esta publicação não pode ser exibida porque o utilizador não está autorizado a visualizá-la.",
|
||||
"status.quote_post_author": "Publicação de {name}",
|
||||
"status.read_more": "Ler mais",
|
||||
"status.reblog": "Impulsionar",
|
||||
"status.reblog_private": "Impulsionar com a visibilidade original",
|
||||
|
||||
@@ -86,10 +86,10 @@
|
||||
"admin.dashboard.retention.average": "В среднем",
|
||||
"admin.dashboard.retention.cohort": "Месяц регистрации",
|
||||
"admin.dashboard.retention.cohort_size": "Новые пользователи",
|
||||
"admin.impact_report.instance_accounts": "Профили учетных записей, которые будут удалены",
|
||||
"admin.impact_report.instance_followers": "Подписчики, которых потеряют наши пользователи",
|
||||
"admin.impact_report.instance_follows": "Подписчики, которых потеряют их пользователи",
|
||||
"admin.impact_report.title": "Резюме воздействия",
|
||||
"admin.impact_report.instance_accounts": "Число профилей, которые будут удалены",
|
||||
"admin.impact_report.instance_followers": "Число подписчиков, которых лишатся наши пользователи",
|
||||
"admin.impact_report.instance_follows": "Число подписчиков, которых лишатся их пользователи",
|
||||
"admin.impact_report.title": "Сводка последствий",
|
||||
"alert.rate_limited.message": "Подождите до {retry_time, time, medium}, прежде чем делать что-либо ещё.",
|
||||
"alert.rate_limited.title": "Слишком много запросов",
|
||||
"alert.unexpected.message": "Произошла непредвиденная ошибка.",
|
||||
@@ -305,8 +305,8 @@
|
||||
"emoji_button.search_results": "Результаты поиска",
|
||||
"emoji_button.symbols": "Символы",
|
||||
"emoji_button.travel": "Путешествия и места",
|
||||
"empty_column.account_featured.me": "Вы ещё ничего не закрепили в своём профиле. Знаете ли вы, что вы можете рекомендовать в этом разделе свои посты, часто используемые вами хэштеги и даже профили друзей?",
|
||||
"empty_column.account_featured.other": "{acct} ещё ничего не закрепил(а) в своём профиле. Знаете ли вы, что вы можете рекомендовать в этом разделе свои посты, часто используемые вами хэштеги и даже профили друзей?",
|
||||
"empty_column.account_featured.me": "Вы ещё ничего не закрепили в своём профиле. Знаете ли вы, что вы можете рекомендовать в этом разделе свои посты, часто используемые вами хештеги и даже профили друзей?",
|
||||
"empty_column.account_featured.other": "{acct} ещё ничего не закрепил(а) в своём профиле. Знаете ли вы, что вы можете рекомендовать в этом разделе свои посты, часто используемые вами хештеги и даже профили друзей?",
|
||||
"empty_column.account_featured_other.unknown": "Этот пользователь ещё ничего не закрепил в своём профиле.",
|
||||
"empty_column.account_hides_collections": "Пользователь предпочёл не раскрывать эту информацию",
|
||||
"empty_column.account_suspended": "Учётная запись заблокирована",
|
||||
@@ -357,7 +357,7 @@
|
||||
"filter_modal.select_filter.title": "Фильтровать этот пост",
|
||||
"filter_modal.title.status": "Фильтровать пост",
|
||||
"filter_warning.matches_filter": "Соответствует фильтру «<span>{title}</span>»",
|
||||
"filtered_notifications_banner.pending_requests": "От {count, plural, =0 {не известных вам людей} one {# возможно вам известного человека} other {# возможно вам известных человек}}",
|
||||
"filtered_notifications_banner.pending_requests": "От {count, plural, =0 {не знакомых вам людей} one {# человека, которого вы можете знать} other {# человек, которых вы можете знать}}",
|
||||
"filtered_notifications_banner.title": "Отфильтрованные уведомления",
|
||||
"firehose.all": "Всё вместе",
|
||||
"firehose.local": "Этот сервер",
|
||||
@@ -392,8 +392,8 @@
|
||||
"generic.saved": "Сохранено",
|
||||
"getting_started.heading": "Добро пожаловать",
|
||||
"hashtag.admin_moderation": "Открыть интерфейс модератора для #{name}",
|
||||
"hashtag.browse": "Обзор постов с хэштегом #{hashtag}",
|
||||
"hashtag.browse_from_account": "Обзор постов от @{name} с хэштегом #{hashtag}",
|
||||
"hashtag.browse": "Обзор постов с хештегом #{hashtag}",
|
||||
"hashtag.browse_from_account": "Обзор постов от @{name} с хештегом #{hashtag}",
|
||||
"hashtag.column_header.tag_mode.all": "и {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "или {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "без {additional}",
|
||||
@@ -454,7 +454,7 @@
|
||||
"interaction_modal.title.reblog": "Продвинуть пост {name}",
|
||||
"interaction_modal.title.reply": "Ответить на пост {name}",
|
||||
"interaction_modal.title.vote": "Голосовать в опросе {name}",
|
||||
"interaction_modal.username_prompt": "Например {example}",
|
||||
"interaction_modal.username_prompt": "Например, {example}",
|
||||
"intervals.full.days": "{number, plural, one {# день} few {# дня} other {# дней}}",
|
||||
"intervals.full.hours": "{number, plural, one {# час} few {# часа} other {# часов}}",
|
||||
"intervals.full.minutes": "{number, plural, one {# минута} few {# минуты} other {# минут}}",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Ndjekës",
|
||||
"account.followers.empty": "Këtë përdorues ende s’e ndjek kush.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} ndjekës} other {{counter} ndjekës}}",
|
||||
"account.followers_you_know_counter": "{counter} që njihni",
|
||||
"account.following": "Ndjekje",
|
||||
"account.following_counter": "{count, plural, one {{counter} i ndjekur} other {{counter} të ndjekur}}",
|
||||
"account.follows.empty": "Ky përdorues ende s’ndjek kënd.",
|
||||
@@ -858,6 +859,12 @@
|
||||
"status.mute_conversation": "Heshtoje bisedën",
|
||||
"status.open": "Zgjeroje këtë mesazh",
|
||||
"status.pin": "Pasqyrojeni në profil",
|
||||
"status.quote_error.not_found": "Ky postim s’mund të shfaqet.",
|
||||
"status.quote_error.pending_approval": "Ky postim është në pritje të miratimit nga autori origjinal.",
|
||||
"status.quote_error.rejected": "Ky postim s’mund të shfaqet, ngaqë autori origjinal nuk lejon citim të tij.",
|
||||
"status.quote_error.removed": "Ky postim u hoq nga autori i tij.",
|
||||
"status.quote_error.unauthorized": "Ky postim s’mund të shfaqet, ngaqë s’jeni i autorizuar ta shihni.",
|
||||
"status.quote_post_author": "Postim nga {name}",
|
||||
"status.read_more": "Lexoni më tepër",
|
||||
"status.reblog": "Përforcojeni",
|
||||
"status.reblog_private": "Përforcim për publikun origjinal",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "Takipçi",
|
||||
"account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} takipçi} other {{counter} takipçi}}",
|
||||
"account.followers_you_know_counter": "bildiğiniz {counter}",
|
||||
"account.following": "Takip Ediliyor",
|
||||
"account.following_counter": "{count, plural, one {{counter} takip edilen} other {{counter} takip edilen}}",
|
||||
"account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "Sohbeti sessize al",
|
||||
"status.open": "Bu gönderiyi genişlet",
|
||||
"status.pin": "Profilimde öne çıkar",
|
||||
"status.quote_error.not_found": "Bu gönderi görüntülenemez.",
|
||||
"status.quote_error.pending_approval": "Bu gönderi özgün yazarın onayını bekliyor.",
|
||||
"status.quote_error.rejected": "Bu gönderi, özgün yazar alıntılanmasına izin vermediği için görüntülenemez.",
|
||||
"status.quote_error.removed": "Bu gönderi yazarı tarafından kaldırıldı.",
|
||||
"status.quote_error.unauthorized": "Bu gönderiyi, yetkiniz olmadığı için görüntüleyemiyorsunuz.",
|
||||
"status.quote_post_author": "{name} gönderisi",
|
||||
"status.read_more": "Devamını okuyun",
|
||||
"status.reblog": "Yeniden paylaş",
|
||||
"status.reblog_private": "Özgün görünürlük ile yeniden paylaş",
|
||||
|
||||
@@ -843,6 +843,7 @@
|
||||
"status.mute": "Приховати @{name}",
|
||||
"status.mute_conversation": "Ігнорувати розмову",
|
||||
"status.open": "Розгорнути допис",
|
||||
"status.quote_post_author": "@{name} опублікував допис",
|
||||
"status.read_more": "Дізнатися більше",
|
||||
"status.reblog": "Поширити",
|
||||
"status.reblog_private": "Поширити для початкової аудиторії",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"account.followers": "跟隨者",
|
||||
"account.followers.empty": "尚未有人跟隨這位使用者。",
|
||||
"account.followers_counter": "被 {count, plural, other {{count} 人}}跟隨",
|
||||
"account.followers_you_know_counter": "{counter} 位您知道的跟隨者",
|
||||
"account.following": "跟隨中",
|
||||
"account.following_counter": "正在跟隨 {count,plural,other {{count} 人}}",
|
||||
"account.follows.empty": "這位使用者尚未跟隨任何人。",
|
||||
@@ -863,6 +864,12 @@
|
||||
"status.mute_conversation": "靜音對話",
|
||||
"status.open": "展開此嘟文",
|
||||
"status.pin": "於個人檔案推薦",
|
||||
"status.quote_error.not_found": "這則嘟文無法被顯示。",
|
||||
"status.quote_error.pending_approval": "此嘟文正在等待原作者審核。",
|
||||
"status.quote_error.rejected": "由於原作者不允許引用,此嘟文無法被顯示。",
|
||||
"status.quote_error.removed": "此嘟文已被其作者移除。",
|
||||
"status.quote_error.unauthorized": "由於您未被授權檢視,此嘟文無法被顯示。",
|
||||
"status.quote_post_author": "由 {name} 發嘟",
|
||||
"status.read_more": "閱讀更多",
|
||||
"status.reblog": "轉嘟",
|
||||
"status.reblog_private": "依照原嘟可見性轉嘟",
|
||||
|
||||
1
app/javascript/material-icons/400-24px/article-fill.svg
Normal file
1
app/javascript/material-icons/400-24px/article-fill.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm80-160h280v-80H280v80Zm0-160h400v-80H280v80Zm0-160h400v-80H280v80Z"/></svg>
|
||||
|
After Width: | Height: | Size: 292 B |
1
app/javascript/material-icons/400-24px/article.svg
Normal file
1
app/javascript/material-icons/400-24px/article.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-280h280v-80H280v80Zm0-160h400v-80H280v80Zm0-160h400v-80H280v80Zm-80 480q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z"/></svg>
|
||||
|
After Width: | Height: | Size: 331 B |
@@ -1 +1,12 @@
|
||||
Files in this directory are Material Symbols icons fetched using the `icons:download` task.
|
||||
Files in this directory are Material Symbols icons fetched using the `icons:download` rake task (see `/lib/tasks/icons.rake`).
|
||||
|
||||
To add another icon, follow these steps:
|
||||
|
||||
- Determine the name of the Material Symbols icon you want to download.
|
||||
You can find a searchable overview of all icons on [https://fonts.google.com/icons].
|
||||
Click on the icon you want to use and find the icon name towards the bottom of the slide-out panel (it'll be something like `icon_name`)
|
||||
- Import the icon in your React component using the following format:
|
||||
`import IconName from '@/material-icons/400-24px/icon_name.svg?react';`
|
||||
- Run `RAILS_ENV=development rails icons:download` to download any newly imported icons.
|
||||
|
||||
The import should now work and the icon should appear when passed to the `<Icon icon={IconName} /> component
|
||||
|
||||
@@ -1421,6 +1421,10 @@ body > [data-popper-placement] {
|
||||
}
|
||||
}
|
||||
|
||||
.status--has-quote .quote-inline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 16px;
|
||||
min-height: 54px;
|
||||
@@ -1491,8 +1495,12 @@ body > [data-popper-placement] {
|
||||
}
|
||||
}
|
||||
|
||||
&--is-quote {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&--in-thread {
|
||||
$thread-margin: 46px + 10px;
|
||||
--thread-margin: calc(46px + 8px);
|
||||
|
||||
border-bottom: 0;
|
||||
|
||||
@@ -1508,16 +1516,16 @@ body > [data-popper-placement] {
|
||||
.hashtag-bar,
|
||||
.content-warning,
|
||||
.filter-warning {
|
||||
margin-inline-start: $thread-margin;
|
||||
width: calc(100% - $thread-margin);
|
||||
margin-inline-start: var(--thread-margin);
|
||||
width: calc(100% - var(--thread-margin));
|
||||
}
|
||||
|
||||
.more-from-author {
|
||||
width: calc(100% - $thread-margin + 2px);
|
||||
width: calc(100% - var(--thread-margin) + 2px);
|
||||
}
|
||||
|
||||
.status__content__read-more-button {
|
||||
margin-inline-start: $thread-margin;
|
||||
margin-inline-start: var(--thread-margin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1873,6 +1881,81 @@ body > [data-popper-placement] {
|
||||
}
|
||||
}
|
||||
|
||||
.status__quote {
|
||||
position: relative;
|
||||
margin-block-start: 16px;
|
||||
margin-inline-start: 36px;
|
||||
border-radius: 8px;
|
||||
color: var(--nested-card-text);
|
||||
background: var(--nested-card-background);
|
||||
border: var(--nested-card-border);
|
||||
|
||||
@media screen and (min-width: $mobile-breakpoint) {
|
||||
margin-inline-start: 56px;
|
||||
}
|
||||
}
|
||||
|
||||
.status__quote--error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.status__quote-author-button {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: inline-flex;
|
||||
width: auto;
|
||||
margin-block-start: 10px;
|
||||
padding: 5px 12px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
letter-spacing: 0;
|
||||
text-decoration: none;
|
||||
color: $highlight-text-color;
|
||||
background: var(--nested-card-background);
|
||||
border: var(--nested-card-border);
|
||||
border-radius: 4px;
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: lighten($highlight-text-color, 4%);
|
||||
color: lighten($highlight-text-color, 4%);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: $ui-button-icon-focus-outline;
|
||||
}
|
||||
}
|
||||
|
||||
.status__quote-icon {
|
||||
position: absolute;
|
||||
inset-block-start: 18px;
|
||||
inset-inline-start: -40px;
|
||||
display: block;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
padding: 5px;
|
||||
color: #6a49ba;
|
||||
z-index: 10;
|
||||
|
||||
.status__quote--error & {
|
||||
inset-block-start: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@media screen and (min-width: $mobile-breakpoint) {
|
||||
inset-inline-start: -50px;
|
||||
}
|
||||
}
|
||||
|
||||
.detailed-status__link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -2170,14 +2253,18 @@ a .account__avatar {
|
||||
|
||||
.avatar-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
--avatar-height: 28px;
|
||||
|
||||
&:not(.avatar-group--compact) {
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
height: var(--avatar-height);
|
||||
overflow-y: clip;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-group--compact {
|
||||
gap: 0;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
& > :not(:first-child) {
|
||||
margin-inline-start: -12px;
|
||||
}
|
||||
@@ -2306,11 +2393,6 @@ a.account__display-name {
|
||||
}
|
||||
}
|
||||
|
||||
.status__avatar {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
.muted {
|
||||
.status__content,
|
||||
.status__content p,
|
||||
@@ -10334,7 +10416,8 @@ noscript {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 8px;
|
||||
|
||||
.logo {
|
||||
width: 16px;
|
||||
@@ -10428,12 +10511,6 @@ noscript {
|
||||
overflow: hidden;
|
||||
container-type: inline-size;
|
||||
|
||||
@container (width < 350px) {
|
||||
&__header time {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -10446,7 +10523,8 @@ noscript {
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px 8px;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
color: $darker-text-color;
|
||||
@@ -10464,6 +10542,13 @@ noscript {
|
||||
time {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
@container (width < 350px) {
|
||||
time,
|
||||
&-separator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10515,6 +10600,7 @@ noscript {
|
||||
line-height: 22px;
|
||||
color: $darker-text-color;
|
||||
-webkit-line-clamp: 4;
|
||||
line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
max-height: none;
|
||||
overflow: hidden;
|
||||
@@ -10670,7 +10756,15 @@ noscript {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&__number {
|
||||
&__numbers,
|
||||
&__familiar-followers {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
&__numbers {
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
color: $secondary-text-color;
|
||||
@@ -10818,9 +10912,9 @@ noscript {
|
||||
.content-warning {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
background: rgba($ui-highlight-color, 0.05);
|
||||
color: $secondary-text-color;
|
||||
border: 1px solid rgba($ui-highlight-color, 0.15);
|
||||
background: var(--nested-card-background);
|
||||
color: var(--nested-card-text);
|
||||
border: var(--nested-card-border);
|
||||
border-radius: 8px;
|
||||
padding: 8px (5px + 8px);
|
||||
position: relative;
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
--rich-text-container-color: rgba(87, 24, 60, 100%);
|
||||
--rich-text-text-color: rgba(255, 175, 212, 100%);
|
||||
--rich-text-decorations-color: rgba(128, 58, 95, 100%);
|
||||
--nested-card-background: color(from #{$ui-highlight-color} srgb r g b / 5%);
|
||||
--nested-card-text: #{$secondary-text-color};
|
||||
--nested-card-border: 1px solid
|
||||
color(from #{$ui-highlight-color} srgb r g b / 15%);
|
||||
--input-placeholder-color: #{$dark-text-color};
|
||||
--input-background-color: var(--surface-variant-background-color);
|
||||
--on-input-color: #{$secondary-text-color};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Fasp::Request
|
||||
COVERED_COMPONENTS = %w(@method @target-uri content-digest).freeze
|
||||
|
||||
def initialize(provider)
|
||||
@provider = provider
|
||||
end
|
||||
@@ -23,55 +25,36 @@ class Fasp::Request
|
||||
url = @provider.url(path)
|
||||
body = body.present? ? body.to_json : ''
|
||||
headers = request_headers(verb, url, body)
|
||||
response = HTTP.headers(headers).send(verb, url, body:)
|
||||
key = Linzer.new_ed25519_key(@provider.server_private_key_pem, @provider.remote_identifier)
|
||||
response = HTTP
|
||||
.headers(headers)
|
||||
.use(http_signature: { key:, covered_components: COVERED_COMPONENTS })
|
||||
.send(verb, url, body:)
|
||||
|
||||
validate!(response)
|
||||
|
||||
response.parse if response.body.present?
|
||||
end
|
||||
|
||||
def request_headers(verb, url, body = '')
|
||||
result = {
|
||||
def request_headers(_verb, _url, body = '')
|
||||
{
|
||||
'accept' => 'application/json',
|
||||
'content-type' => 'application/json',
|
||||
'content-digest' => content_digest(body),
|
||||
}
|
||||
result.merge(signature_headers(verb, url, result))
|
||||
end
|
||||
|
||||
def content_digest(body)
|
||||
"sha-256=:#{OpenSSL::Digest.base64digest('sha256', body || '')}:"
|
||||
end
|
||||
|
||||
def signature_headers(verb, url, headers)
|
||||
linzer_request = Linzer.new_request(verb, url, {}, headers)
|
||||
message = Linzer::Message.new(linzer_request)
|
||||
key = Linzer.new_ed25519_key(@provider.server_private_key_pem, @provider.remote_identifier)
|
||||
signature = Linzer.sign(key, message, %w(@method @target-uri content-digest))
|
||||
Linzer::Signer.send(:populate_parameters, key, {})
|
||||
|
||||
signature.to_h
|
||||
end
|
||||
|
||||
def validate!(response)
|
||||
content_digest_header = response.headers['content-digest']
|
||||
raise Mastodon::SignatureVerificationError, 'content-digest missing' if content_digest_header.blank?
|
||||
raise Mastodon::SignatureVerificationError, 'content-digest does not match' if content_digest_header != content_digest(response.body)
|
||||
raise Mastodon::SignatureVerificationError, 'signature-input is missing' if response.headers['signature-input'].blank?
|
||||
|
||||
signature_input = response.headers['signature-input']&.encode('UTF-8')
|
||||
raise Mastodon::SignatureVerificationError, 'signature-input is missing' if signature_input.blank?
|
||||
|
||||
linzer_response = Linzer.new_response(
|
||||
response.body,
|
||||
response.status,
|
||||
{
|
||||
'content-digest' => content_digest_header,
|
||||
'signature-input' => signature_input,
|
||||
'signature' => response.headers['signature'],
|
||||
}
|
||||
)
|
||||
message = Linzer::Message.new(linzer_response)
|
||||
key = Linzer.new_ed25519_public_key(@provider.provider_public_key_pem)
|
||||
signature = Linzer::Signature.build(message.headers)
|
||||
Linzer.verify(key, message, signature)
|
||||
Linzer.verify!(response, key:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,6 +42,6 @@ class Rule < ApplicationRecord
|
||||
|
||||
def translation_for(locale)
|
||||
@cached_translations ||= {}
|
||||
@cached_translations[locale] ||= translations.find_by(language: locale) || RuleTranslation.new(language: locale, text: text, hint: hint)
|
||||
@cached_translations[locale] ||= translations.where(language: [locale, locale.to_s.split('-').first]).order('length(language) desc').first || RuleTranslation.new(language: locale, text: text, hint: hint)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -73,7 +73,6 @@ class User < ApplicationRecord
|
||||
ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days.freeze
|
||||
|
||||
devise :two_factor_authenticatable,
|
||||
otp_secret_encryption_key: Rails.configuration.x.otp_secret,
|
||||
otp_secret_length: 32
|
||||
|
||||
devise :two_factor_backupable,
|
||||
|
||||
@@ -4,13 +4,11 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService
|
||||
include JsonLdHelper
|
||||
|
||||
def call(account, **options)
|
||||
return if account.featured_collection_url.blank? || account.suspended? || account.local?
|
||||
return if (account.featured_collection_url.blank? && options[:collection].blank?) || account.suspended? || account.local?
|
||||
|
||||
@account = account
|
||||
@options = options
|
||||
@json = fetch_resource(@account.featured_collection_url, true, local_follower)
|
||||
|
||||
return unless supported_context?(@json)
|
||||
@json = fetch_collection(options[:collection].presence || @account.featured_collection_url)
|
||||
|
||||
process_items(collection_items(@json))
|
||||
end
|
||||
|
||||
@@ -57,7 +57,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
after_suspension_change! if suspension_changed?
|
||||
|
||||
unless @options[:only_key] || @account.suspended?
|
||||
check_featured_collection! if @account.featured_collection_url.present?
|
||||
check_featured_collection! if @json['featured'].present?
|
||||
check_featured_tags_collection! if @json['featuredTags'].present?
|
||||
check_links! if @account.fields.any?(&:requires_verification?)
|
||||
end
|
||||
@@ -121,7 +121,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
end
|
||||
|
||||
def set_immediate_attributes!
|
||||
@account.featured_collection_url = @json['featured'] || ''
|
||||
@account.featured_collection_url = valid_collection_uri(@json['featured'])
|
||||
@account.display_name = @json['name'] || ''
|
||||
@account.note = @json['summary'] || ''
|
||||
@account.locked = @json['manuallyApprovesFollowers'] || false
|
||||
@@ -186,7 +186,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
end
|
||||
|
||||
def check_featured_collection!
|
||||
ActivityPub::SynchronizeFeaturedCollectionWorker.perform_async(@account.id, { 'hashtag' => @json['featuredTags'].blank?, 'request_id' => @options[:request_id] })
|
||||
ActivityPub::SynchronizeFeaturedCollectionWorker.perform_async(@account.id, { 'hashtag' => @json['featuredTags'].blank?, 'collection' => @json['featured'], 'request_id' => @options[:request_id] })
|
||||
end
|
||||
|
||||
def check_featured_tags_collection!
|
||||
|
||||
@@ -19,7 +19,7 @@ class FetchLinkCardService < BaseService
|
||||
@status = status
|
||||
@original_url = parse_urls
|
||||
|
||||
return if @original_url.nil? || @status.with_preview_card?
|
||||
return if @original_url.nil? || @status.with_preview_card? || @status.with_media? || @status.quote.present?
|
||||
|
||||
@url = @original_url.to_s
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
= theme_style_tags current_theme
|
||||
= vite_client_tag
|
||||
= vite_react_refresh_tag
|
||||
= vite_polyfills_tag
|
||||
-# Needed for the wicg-inert polyfill. It needs to be on it's own <style> tag, with this `id`
|
||||
= vite_stylesheet_tag 'styles/entrypoints/inert.scss', media: 'all', id: 'inert-style' # TODO: flavour
|
||||
= flavoured_vite_typescript_tag 'common.ts', crossorigin: 'anonymous'
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
= vite_client_tag
|
||||
= vite_react_refresh_tag
|
||||
= vite_polyfills_tag
|
||||
= theme_style_tags current_theme
|
||||
= vite_preload_file_tag "mastodon/locales/#{I18n.locale}.json" # TODO: fix preload for flavour
|
||||
= render_initial_state
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
|
||||
= vite_client_tag
|
||||
= vite_react_refresh_tag
|
||||
= vite_polyfills_tag
|
||||
= theme_style_tags current_theme
|
||||
= flavoured_vite_typescript_tag 'error.ts', crossorigin: 'anonymous'
|
||||
%body.error
|
||||
|
||||
@@ -6,7 +6,7 @@ class ActivityPub::SynchronizeFeaturedCollectionWorker
|
||||
sidekiq_options queue: 'pull', lock: :until_executed, lock_ttl: 1.day.to_i
|
||||
|
||||
def perform(account_id, options = {})
|
||||
options = { note: true, hashtag: false }.deep_merge(options.deep_symbolize_keys)
|
||||
options = { note: true, hashtag: false }.deep_merge(options.symbolize_keys)
|
||||
|
||||
ActivityPub::FetchFeaturedCollectionService.new.call(Account.find(account_id), **options)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
|
||||
@@ -84,9 +84,6 @@ Rails.application.configure do
|
||||
# Otherwise, use letter_opener, which launches a browser window to view sent mail.
|
||||
config.action_mailer.delivery_method = ENV['HEROKU'] || ENV['VAGRANT'] || ENV['REMOTE_DEV'] ? :letter_opener_web : :letter_opener
|
||||
|
||||
# TODO: Remove once devise-two-factor data migration complete
|
||||
config.x.otp_secret = ENV.fetch('OTP_SECRET', '1fc2b87989afa6351912abeebe31ffc5c476ead9bf8b3d74cbc4a302c7b69a45b40b1bbef3506ddad73e942e15ed5ca4b402bf9a66423626051104f4b5f05109')
|
||||
|
||||
# Raise error when a before_action's only/except options reference missing actions.
|
||||
config.action_controller.raise_on_missing_callback_actions = true
|
||||
|
||||
|
||||
32
config/initializers/linzer.rb
Normal file
32
config/initializers/linzer.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'linzer/http/signature_feature'
|
||||
require 'linzer/message/adapter/http_gem/response'
|
||||
|
||||
module Linzer::Message::Adapter
|
||||
module ActionDispatch
|
||||
class Response < Linzer::Message::Adapter::Abstract
|
||||
def initialize(operation, **_options) # rubocop:disable Lint/MissingSuper
|
||||
@operation = operation
|
||||
end
|
||||
|
||||
def header(name)
|
||||
@operation.headers[name]
|
||||
end
|
||||
|
||||
def attach!(signature)
|
||||
signature.to_h.each { |h, v| @operation.headers[h] = v }
|
||||
end
|
||||
|
||||
# Incomplete, but sufficient for FASP
|
||||
def [](field_name)
|
||||
return @operation.status if field_name == '@status'
|
||||
|
||||
@operation.headers[field_name]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Linzer::Message.register_adapter(HTTP::Response, Linzer::Message::Adapter::HTTPGem::Response)
|
||||
Linzer::Message.register_adapter(ActionDispatch::Response, Linzer::Message::Adapter::ActionDispatch::Response)
|
||||
@@ -42,7 +42,7 @@ if ENV['S3_ENABLED'] == 'true'
|
||||
s3_protocol = ENV.fetch('S3_PROTOCOL') { 'https' }
|
||||
s3_hostname = ENV.fetch('S3_HOSTNAME') { "s3-#{s3_region}.amazonaws.com" }
|
||||
|
||||
Paperclip::Attachment.default_options[:path] = ENV.fetch('S3_KEY_PREFIX') + "/#{PATH}" if ENV.has_key?('S3_KEY_PREFIX')
|
||||
Paperclip::Attachment.default_options[:path] = ENV.fetch('S3_KEY_PREFIX') + "/#{PATH}" if ENV.key?('S3_KEY_PREFIX')
|
||||
|
||||
Paperclip::Attachment.default_options.merge!(
|
||||
storage: :s3,
|
||||
@@ -74,7 +74,7 @@ if ENV['S3_ENABLED'] == 'true'
|
||||
|
||||
Paperclip::Attachment.default_options[:s3_permissions] = ->(*) {} if ENV['S3_PERMISSION'] == ''
|
||||
|
||||
if ENV.has_key?('S3_ENDPOINT')
|
||||
if ENV.key?('S3_ENDPOINT')
|
||||
Paperclip::Attachment.default_options[:s3_options].merge!(
|
||||
endpoint: ENV['S3_ENDPOINT'],
|
||||
force_path_style: ENV['S3_OVERRIDE_PATH_STYLE'] != 'true'
|
||||
@@ -83,14 +83,14 @@ if ENV['S3_ENABLED'] == 'true'
|
||||
Paperclip::Attachment.default_options[:url] = ':s3_path_url'
|
||||
end
|
||||
|
||||
if ENV.has_key?('S3_ALIAS_HOST') || ENV.has_key?('S3_CLOUDFRONT_HOST')
|
||||
if ENV.key?('S3_ALIAS_HOST') || ENV.key?('S3_CLOUDFRONT_HOST')
|
||||
Paperclip::Attachment.default_options.merge!(
|
||||
url: ':s3_alias_url',
|
||||
s3_host_alias: ENV['S3_ALIAS_HOST'] || ENV['S3_CLOUDFRONT_HOST']
|
||||
)
|
||||
end
|
||||
|
||||
Paperclip::Attachment.default_options[:s3_headers]['X-Amz-Storage-Class'] = ENV['S3_STORAGE_CLASS'] if ENV.has_key?('S3_STORAGE_CLASS')
|
||||
Paperclip::Attachment.default_options[:s3_headers]['X-Amz-Storage-Class'] = ENV['S3_STORAGE_CLASS'] if ENV.key?('S3_STORAGE_CLASS')
|
||||
|
||||
# Some S3-compatible providers might not actually be compatible with some APIs
|
||||
# used by kt-paperclip, see https://github.com/mastodon/mastodon/issues/16822
|
||||
@@ -153,7 +153,7 @@ elsif ENV['AZURE_ENABLED'] == 'true'
|
||||
container: ENV['AZURE_CONTAINER_NAME'],
|
||||
}
|
||||
)
|
||||
if ENV.has_key?('AZURE_ALIAS_HOST')
|
||||
if ENV.key?('AZURE_ALIAS_HOST')
|
||||
Paperclip::Attachment.default_options.merge!(
|
||||
url: ':azure_alias_url',
|
||||
azure_host_alias: ENV['AZURE_ALIAS_HOST']
|
||||
|
||||
@@ -271,10 +271,13 @@ br:
|
||||
view_devops: DevOps
|
||||
title: Rolloù
|
||||
rules:
|
||||
add_translation: Ouzhpennañ un droidigezh
|
||||
delete: Dilemel
|
||||
edit: Kemmañ ar reolenn
|
||||
move_down: D'an traoñ
|
||||
move_up: D'ar c'hrec'h
|
||||
translation: Troidigezh
|
||||
translations: Troidigezhioù
|
||||
settings:
|
||||
about:
|
||||
title: Diwar-benn
|
||||
|
||||
@@ -5,7 +5,7 @@ cs:
|
||||
contact_missing: Nenastaveno
|
||||
contact_unavailable: Neuvedeno
|
||||
hosted_on: Mastodon na doméně %{domain}
|
||||
title: O aplikaci
|
||||
title: O službě
|
||||
accounts:
|
||||
followers:
|
||||
few: Sledující
|
||||
@@ -814,11 +814,17 @@ cs:
|
||||
title: Role
|
||||
rules:
|
||||
add_new: Přidat pravidlo
|
||||
add_translation: Přidat překlad
|
||||
delete: Smazat
|
||||
description_html: Přestože většina tvrdí, že četla a souhlasí s podmínkami služby, lidé je obvykle nepročtou dříve, než vznikne problém. <strong>Usnadněte prohlížení pravidel vašeho serveru jejich poskytnutím v odrážkovém seznamu.</strong> Snažte se držet jednotlivá pravidla krátká a jednoduchá, ale zároveň je nerozdělovat do mnoha samostatných položek.
|
||||
edit: Upravit pravidlo
|
||||
empty: Zatím nebyla definována žádná pravidla serveru.
|
||||
move_down: Posunout dolů
|
||||
move_up: Přesunout nahoru
|
||||
title: Pravidla serveru
|
||||
translation: Překlad
|
||||
translations: Překlady
|
||||
translations_explanation: Volitelně můžete též přidat překlady pravidel. Výchozí hodnota bude zobrazena, pokud není k dispozici žádná přeložená verze. Vždy se ujistěte, že jakýkoliv překlad je synchronizován s výchozí hodnotou.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Spravovat pravidla serveru
|
||||
|
||||
@@ -842,11 +842,17 @@ cy:
|
||||
title: Rolau
|
||||
rules:
|
||||
add_new: Ychwanegu rheol
|
||||
add_translation: Ychwanegu cyfieithiad
|
||||
delete: Dileu
|
||||
description_html: Er bod y rhan fwyaf yn honni eu bod wedi darllen ac yn cytuno i'r telerau gwasanaeth, fel arfer nid yw pobl yn darllen y cyfan tan ar ôl i broblem godi. <strong>Gwnewch hi'n haws i bobl weld rheolau eich gweinydd yn fras trwy eu darparu mewn rhestr pwyntiau bwled fflat.</strong> Ceisiwch gadw'r rheolau unigol yn fyr ac yn syml, ond ceisiwch beidio â'u rhannu'n nifer o eitemau ar wahân chwaith.
|
||||
edit: Golygu rheol
|
||||
empty: Nid oes unrhyw reolau gweinydd wedi'u diffinio eto.
|
||||
move_down: Symud i lawr
|
||||
move_up: Symud i fyny
|
||||
title: Rheolau'r gweinydd
|
||||
translation: Cyfieithiad
|
||||
translations: Cyfieithiadau
|
||||
translations_explanation: Gallwch ychwanegu cyfieithiadau ar gyfer y rheolau yn ôl eich dewis. Bydd y gwerth rhagosodedig yn cael ei ddangos os nad oes fersiwn wedi'i chyfieithu ar gael. Gwnewch yn siŵr bob amser bod unrhyw gyfieithiad sy'n cael ei ddarparu'n gyson â'r gwerth rhagosodedig.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Rheoli rheolau gweinydd
|
||||
|
||||
@@ -786,6 +786,7 @@ da:
|
||||
title: Roller
|
||||
rules:
|
||||
add_new: Tilføj regel
|
||||
add_translation: Tilføj oversættelse
|
||||
delete: Slet
|
||||
description_html: Mens de fleste hævder at have læst og accepteret tjenestevilkårene, så læser folk normalt ikke disse, før et problem er opstået. <strong>Gør det lettere med ét blik at se din servers regler ved at opliste disse på en punktliste.</strong> Forsøg at holde individuelle regler korte og enkle, men undgå også at opdele dem i mange separate underpunkter.
|
||||
edit: Redigér regel
|
||||
@@ -793,6 +794,9 @@ da:
|
||||
move_down: Flyt ned
|
||||
move_up: Flyt op
|
||||
title: Serverregler
|
||||
translation: Oversættelse
|
||||
translations: Oversættelser
|
||||
translations_explanation: Man kan valgfrit tilføje oversættelser af reglerne. Standardindholdet vil fremgå, såfremt ingen oversat version findes. Tilsikr venligst, at enhver given oversættelse er synket med standardindholdet.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Håndtér serverregler
|
||||
|
||||
@@ -783,11 +783,17 @@ de:
|
||||
title: Rollen
|
||||
rules:
|
||||
add_new: Regel hinzufügen
|
||||
add_translation: Übersetzung hinzufügen
|
||||
delete: Löschen
|
||||
description_html: Während die meisten behaupten, die Nutzungsbedingungen tatsächlich gelesen zu haben, bekommen viele sie erst nach einem Problem mit. <strong>Vereinfache und reduziere daher die Serverregeln mit Stichpunkten.</strong> Versuche dabei, die einzelnen Vorgaben kurz und einfach zu halten, aber vermeide, sie in viele verschiedene Elemente aufzuteilen.
|
||||
edit: Regel bearbeiten
|
||||
empty: Es wurden bisher keine Serverregeln definiert.
|
||||
move_down: Abwärts
|
||||
move_up: Aufwärts
|
||||
title: Serverregeln
|
||||
translation: Übersetzung
|
||||
translations: Übersetzungen
|
||||
translations_explanation: Du kannst Serverregeln übersetzen. Der Standardwert wird angezeigt, wenn keine übersetzte Version verfügbar ist. Bitte vergewissere dich, dass jede Übersetzung mit dem Standardwert übereinstimmt.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Serverregeln verwalten
|
||||
@@ -796,7 +802,7 @@ de:
|
||||
title: Über
|
||||
allow_referrer_origin:
|
||||
desc: Klicken Nutzer*innen auf Links zu externen Seiten, kann der Browser die Adresse deines Mastodon-Servers als Referrer (Verweis) übermitteln. Diese Option sollte deaktiviert werden, wenn Nutzer*innen dadurch eindeutig identifiziert würden – z. B. wenn es sich um einen privaten Mastodon-Server handelt.
|
||||
title: Externen Seiten erlauben, diesen Mastodon-Server als Traffic-Quelle zu sehen
|
||||
title: Externen Seiten erlauben, diesen Mastodon-Server als Quelle zu sehen
|
||||
appearance:
|
||||
preamble: Passe das Webinterface von Mastodon an.
|
||||
title: Design
|
||||
|
||||
@@ -786,6 +786,7 @@ es-AR:
|
||||
title: Roles
|
||||
rules:
|
||||
add_new: Agregar regla
|
||||
add_translation: Agregar traducción
|
||||
delete: Eliminar
|
||||
description_html: Aunque la mayoría afirma haber leído y aceptado los términos del servicio, normalmente la gente no los revisa hasta después de que surge un problema. <strong>Hacé que sea más fácil ver las reglas de tu servidor, de un vistazo, disponiéndolas en una lista por puntos.</strong> Tratá de hacer cada regla corta y sencilla, pero no de dividirlas en muchos temas individuales.
|
||||
edit: Editar regla
|
||||
@@ -793,6 +794,9 @@ es-AR:
|
||||
move_down: Bajar
|
||||
move_up: Subir
|
||||
title: Reglas del servidor
|
||||
translation: Traducción
|
||||
translations: Traducciones
|
||||
translations_explanation: Opcionalmente, podés agregar traducciones para las reglas. El valor predeterminado se mostrará si no hay una versión traducida disponible. Por favor, asegurate siempre de que cualquier traducción proporcionada esté sincronizada con el valor predeterminado.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Administrar reglas del servidor
|
||||
|
||||
@@ -786,11 +786,17 @@ es-MX:
|
||||
title: Roles
|
||||
rules:
|
||||
add_new: Añadir norma
|
||||
add_translation: Añadir traducción
|
||||
delete: Eliminar
|
||||
description_html: Aunque la mayoría afirma haber leído y estar de acuerdo con los términos de servicio, la gente normalmente no los lee hasta después de que surja algún problema. <strong>Haz que sea más fácil ver las normas de tu servidor de un vistazo estipulándolas en una lista de puntos.</strong> Intenta que cada norma sea corta y sencilla, pero sin estar divididas en muchos puntos.
|
||||
edit: Editar norma
|
||||
empty: Aún no se han definido las normas del servidor.
|
||||
move_down: Mover hacia abajo
|
||||
move_up: Mover hacia arriba
|
||||
title: Normas del servidor
|
||||
translation: Traducción
|
||||
translations: Traducciones
|
||||
translations_explanation: Opcionalmente, puedes añadir traducciones para las reglas. Se mostrará el valor por defecto si no hay ninguna versión traducida disponible. Por favor, asegúrate siempre de que cualquier traducción proporcionada esté sincronizada con el valor por defecto.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Administrar reglas del servidor
|
||||
@@ -799,6 +805,7 @@ es-MX:
|
||||
title: Acerca de
|
||||
allow_referrer_origin:
|
||||
desc: Cuando tus usuarios cliquen en enlaces a sitios externos, su navegador podría enviar la dirección de su servidor de Mastodon como referencia. Deshabilita esto si identifica a tus usuarios unívocamente, por ejemplo, si este es un servidor de Mastodon personal.
|
||||
title: Permitir que sitios externos vean su servidor Mastodon como fuente de tráfico
|
||||
appearance:
|
||||
preamble: Personalizar la interfaz web de Mastodon.
|
||||
title: Apariencia
|
||||
|
||||
@@ -786,11 +786,17 @@ es:
|
||||
title: Roles
|
||||
rules:
|
||||
add_new: Añadir norma
|
||||
add_translation: Añadir traducción
|
||||
delete: Eliminar
|
||||
description_html: Aunque la mayoría afirma haber leído y estar de acuerdo con los términos de servicio, la gente normalmente no los lee hasta después de que surja algún problema. <strong>Haz que sea más fácil ver las normas de tu servidor de un vistazo estipulándolas en una lista de puntos.</strong> Intenta que cada norma sea corta y sencilla, pero sin estar divididas en muchos puntos.
|
||||
edit: Editar norma
|
||||
empty: Aún no se han definido las normas del servidor.
|
||||
move_down: Mover hacia abajo
|
||||
move_up: Mover hacia arriba
|
||||
title: Normas del servidor
|
||||
translation: Traducción
|
||||
translations: Traducciones
|
||||
translations_explanation: Opcionalmente, puedes añadir traducciones para las reglas. Se mostrará el valor por defecto si no hay ninguna versión traducida disponible. Por favor, asegúrate siempre de que cualquier traducción proporcionada esté sincronizada con el valor por defecto.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Administrar reglas del servidor
|
||||
@@ -799,6 +805,7 @@ es:
|
||||
title: Acerca de
|
||||
allow_referrer_origin:
|
||||
desc: Cuando tus usuarios cliquen en enlaces a sitios externos, su navegador podría enviar la dirección de su servidor de Mastodon como referencia. Deshabilita esto si identifica a tus usuarios unívocamente, por ejemplo, si este es un servidor de Mastodon personal.
|
||||
title: Permitir que sitios externos vean su servidor Mastodon como fuente de tráfico
|
||||
appearance:
|
||||
preamble: Personalizar la interfaz web de Mastodon.
|
||||
title: Apariencia
|
||||
|
||||
@@ -486,13 +486,17 @@ fi:
|
||||
created_at: Luotu
|
||||
delete: Poista
|
||||
ip: IP-osoite
|
||||
request_body: Pyynnön sisältö
|
||||
title: Aloita takaisinkutsujen vianetsintä
|
||||
providers:
|
||||
active: Käytössä
|
||||
base_url: Perus-URL
|
||||
callback: Takaisinkutsu
|
||||
delete: Poista
|
||||
edit: Muokkaa palveluntarjoajaa
|
||||
finish_registration: Viimeistele rekisteröinti
|
||||
name: Nimi
|
||||
providers: Palveluntarjoajat
|
||||
public_key_fingerprint: Julkisen avaimen sormenjälki
|
||||
registration_requested: Rekisteröintiä pyydetty
|
||||
registrations:
|
||||
@@ -780,6 +784,7 @@ fi:
|
||||
title: Roolit
|
||||
rules:
|
||||
add_new: Lisää sääntö
|
||||
add_translation: Lisää käännös
|
||||
delete: Poista
|
||||
description_html: Vaikka useimmat väittävät, että ovat lukeneet ja hyväksyneet käyttöehdot, niin yleensä ihmiset eivät lue niitä läpi ennen kuin ilmenee ongelma. <strong>Helpota palvelimen sääntöjen näkemistä yhdellä silmäyksellä tarjoamalla ne tiiviissä luettelossa.</strong> Yritä pitää säännöt lyhyinä ja yksinkertaisina, mutta yritä olla jakamatta niitä useisiin erillisiin kohtiin.
|
||||
edit: Muokkaa sääntöä
|
||||
@@ -787,6 +792,9 @@ fi:
|
||||
move_down: Siirrä alaspäin
|
||||
move_up: Siirrä ylöspäin
|
||||
title: Palvelimen säännöt
|
||||
translation: Käännös
|
||||
translations: Käännökset
|
||||
translations_explanation: Voit halutessasi lisätä säännöille käännöksiä. Jos käännettyä versiota ei ole saatavilla, näytetään oletusarvo. Varmista aina, että tarjoamasi käännös on linjassa oletusarvon kanssa.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Hallitse palvelimen sääntöjä
|
||||
|
||||
@@ -786,6 +786,7 @@ fo:
|
||||
title: Leiklutir
|
||||
rules:
|
||||
add_new: Ger nýggja reglu
|
||||
add_translation: Legg umseting afturat
|
||||
delete: Strika
|
||||
description_html: Sjálvt um tey flestu vilja vera við, at tey hava lisið og tikið undir við tænastutreytunum, so lesa tey flestu tær ikki fyrr enn eftir at ein trupulleiki stingur seg upp. <strong>Ger tað lættari at skimma ígjøgnum ambætarareglurnar við at veita tær í einum fløtum punkt-lista.</strong> Royn at gera einstøku reglurnar stuttar og einfaldar, samstundis sum at tær ikki blíva pettaðar ov nógv sundur.
|
||||
edit: Broyt reglur
|
||||
@@ -793,6 +794,9 @@ fo:
|
||||
move_down: Flyt niður
|
||||
move_up: Flyt upp
|
||||
title: Ambætarareglur
|
||||
translation: Umseting
|
||||
translations: Umsetingar
|
||||
translations_explanation: Tað er valfrítt at leggja umsetingar afturat fyri reglurnar. Sjálvsetta svarið verður víst, um eingin umsett útgáva er tøk. Vinarliga tryggja, at umsetingar samsvara við sjálvsetta svarið.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Stýr ambætarareglum
|
||||
|
||||
@@ -832,6 +832,8 @@ ga:
|
||||
description_html: Cé go maíonn a bhformhór gur léigh siad agus go n-aontaíonn siad leis na téarmaí seirbhíse, de ghnáth ní léann daoine tríd go dtí go dtagann fadhb chun cinn. <strong>Déan rialacha do fhreastalaí a fheiceáil go sracfhéachaint trí iad a sholáthar i liosta comhréidh de phointe urchair.</strong> Déan iarracht rialacha aonair a choinneáil gearr simplí, ach déan iarracht gan iad a roinnt ina go leor míreanna ar leith ach an oiread.
|
||||
edit: Cuir riail in eagar
|
||||
empty: Níl aon rialacha freastalaí sainmhínithe fós.
|
||||
move_down: Bog síos
|
||||
move_up: Bog suas
|
||||
title: Rialacha freastalaí
|
||||
settings:
|
||||
about:
|
||||
@@ -839,6 +841,9 @@ ga:
|
||||
preamble: Cuir eolas domhain ar fáil faoin gcaoi a n-oibrítear, a ndéantar modhnóireacht agus maoiniú ar an bhfreastalaí.
|
||||
rules_hint: Tá réimse tiomnaithe rialacha ann a bhfuiltear ag súil go gcloífidh d’úsáideoirí leis.
|
||||
title: Faoi
|
||||
allow_referrer_origin:
|
||||
desc: Nuair a chliceálann d’úsáideoirí ar naisc chuig suíomhanna seachtracha, féadfaidh a mbrabhsálaí seoladh do fhreastalaí Mastodon a sheoladh mar an tagairt. Díchumasaigh é seo má shainaithníonn sé seo d’úsáideoirí go huathúil, e.g. más freastalaí pearsanta Mastodon é seo.
|
||||
title: Lig do shuíomhanna seachtracha do fhreastalaí Mastodon a fheiceáil mar fhoinse tráchta
|
||||
appearance:
|
||||
preamble: Saincheap comhéadan gréasáin Mastodon.
|
||||
title: Cuma
|
||||
@@ -858,6 +863,7 @@ ga:
|
||||
discovery:
|
||||
follow_recommendations: Lean na moltaí
|
||||
preamble: Tá sé ríthábhachtach dromchla a chur ar ábhar suimiúil chun úsáideoirí nua a chur ar bord nach bhfuil aithne acu ar dhuine ar bith Mastodon. Rialú conas a oibríonn gnéithe fionnachtana éagsúla ar do fhreastalaí.
|
||||
privacy: Príobháideacht
|
||||
profile_directory: Eolaire próifíle
|
||||
public_timelines: Amlínte poiblí
|
||||
publish_statistics: Staitisticí a fhoilsiú
|
||||
|
||||
@@ -786,6 +786,7 @@ gl:
|
||||
title: Roles
|
||||
rules:
|
||||
add_new: Engadir regra
|
||||
add_translation: Engadir tradución
|
||||
delete: Eliminar
|
||||
description_html: Aínda que a maioría di que leu e acepta as condicións do servizo, normalmente non as lemos ata que xurde un problema. <strong>Facilita a visualización das regras do servidor mostrándoas nunha lista de puntos.</strong> Intenta manter as regras individuais curtas e simples, mais non dividilas en demasiados elementos separados.
|
||||
edit: Editar regra
|
||||
@@ -793,6 +794,9 @@ gl:
|
||||
move_down: Baixar
|
||||
move_up: Subir
|
||||
title: Regras do servidor
|
||||
translation: Tradución
|
||||
translations: Traducións
|
||||
translations_explanation: De xeito optativo podes engadir traducións das normas. Mostrarase o valor por defecto se non hai unha tradución dispoñible. Pon coidado en que a versión traducida estea ao día respecto da versión por defecto.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Xestionar regras do servidor
|
||||
|
||||
@@ -814,6 +814,7 @@ he:
|
||||
title: תפקידים
|
||||
rules:
|
||||
add_new: הוספת כלל
|
||||
add_translation: הוספת תרגום
|
||||
delete: מחיקה
|
||||
description_html: בעוד הרוב טוען שקרא והסכים לתנאי השימוש, אנשים לא נוטים לקרוא אותם עד הסוף עד שמתעוררת בעיה. <strong>כדי שיקל לראות את כללי השרת במבט, יש לספקם כרשימת נקודות.</strong> כדאי לשמור על הכללים קצרים ופשוטים, אבל מאידך גם לא לפצל אותם ליותר מדי נקודות נפרדות.
|
||||
edit: עריכת כלל
|
||||
@@ -821,6 +822,9 @@ he:
|
||||
move_down: הזזה למטה
|
||||
move_up: הזזה למעלה
|
||||
title: כללי שרת
|
||||
translation: תרגום
|
||||
translations: תרגומים
|
||||
translations_explanation: באפשרותך להוסיף גרסאות מתורגמות לשפות נוספות של תנאי השימוש. ברירת המחדף תופיע אם גרסאות מתורגמות אינן בנמצא. עליך לוודא כי הגרסאות המתורגמות מעודכנות יחד עם שפת ברירת המחדל.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: ניהול כללי שרת
|
||||
|
||||
@@ -786,6 +786,7 @@ hu:
|
||||
title: Szerepek
|
||||
rules:
|
||||
add_new: Szabály hozzáadása
|
||||
add_translation: Fordítás hozzáadása
|
||||
delete: Törlés
|
||||
description_html: Bár a többség azt állítja, hogy elolvasták és egyetértenek a felhasználói feltételekkel, általában ez nem teljesül, amíg egy probléma elő nem jön. <strong>Tedd könnyebbé a kiszolgálód szabályainak áttekintését azzal, hogy pontokba foglalod azt egy listában.</strong> Az egyes szabályok legyenek rövidek és egyszerűek. Próbáld meg nem túl sok önálló pontra darabolni őket.
|
||||
edit: Szabály szerkesztése
|
||||
@@ -793,6 +794,9 @@ hu:
|
||||
move_down: Mozgás lefelé
|
||||
move_up: Mozgatás felfelé
|
||||
title: Kiszolgáló szabályai
|
||||
translation: Fordítás
|
||||
translations: Fordítások
|
||||
translations_explanation: Fordításokat is hozzáadhatóak a szabályokhoz. Az alapértelmezett érték lesz megjelenítve, ha nem érhető el lefordított változat. Győződj meg arról, hogy a megadott fordítás szinkronban van az alapértelmezett értékkel.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Kiszolgáló szabályainak kezelése
|
||||
|
||||
@@ -786,6 +786,7 @@ is:
|
||||
title: Hlutverk
|
||||
rules:
|
||||
add_new: Skrá reglu
|
||||
add_translation: Bæta við þýðingu
|
||||
delete: Eyða
|
||||
description_html: Þó að flestir segist hafa lesið og samþykkt þjónustuskilmála, er fólk samt gjarnt á að lesa slíkar upplýsingar ekki til enda fyrr en upp koma einhver vandamál. <strong>Gerðu fólki auðvelt að sjá mikilvægustu reglurnar með því að setja þær fram í flötum punktalista.</strong> Reyndu að hafa hverja reglu stutta og skýra, en ekki vera heldur að skipta þeim upp í mörg aðskilin atriði.
|
||||
edit: Breyta reglu
|
||||
@@ -793,6 +794,9 @@ is:
|
||||
move_down: Færa niður
|
||||
move_up: Færa upp
|
||||
title: Reglur netþjónsins
|
||||
translation: Þýðing
|
||||
translations: Þýðingar
|
||||
translations_explanation: Þú getur valið að bæta við þýðingum fyrir reglurnar. Sjálfgefna gildið verður birt ef engin þýðing er fyrir hendi. Gakktu úr skugga um að uppgefnar þýðingar sé alltaf í samræmi við sjálfgefna gildið.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Sýsla með reglur netþjónsins
|
||||
|
||||
@@ -774,17 +774,26 @@ ko:
|
||||
title: 역할
|
||||
rules:
|
||||
add_new: 규칙 추가
|
||||
add_translation: 번역 추가
|
||||
delete: 삭제
|
||||
description_html: 대부분의 사람들은 서비스 약관을 읽고 동의한다고 밝힙니다만, 대개 문제가 발생하기 전까지는 약관을 자세히 읽어보지 않습니다. <strong>서버의 운영원칙을 단일한 글머리 기호 목록으로 제공하여 한 눈에 쉽게 확인할 수 있도록 하세요.</strong> 각 규칙을 짧고 간결하게 유지하되, 많은 항목으로 나누지 않도록 하세요.
|
||||
edit: 규칙 수정
|
||||
empty: 아직 정의된 서버 규칙이 없습니다.
|
||||
move_down: 아래로 이동
|
||||
move_up: 위로 이동
|
||||
title: 서버 규칙
|
||||
translation: 번역
|
||||
translations: 번역
|
||||
translations_explanation: 규칙에 대한 번역을 추가할 수 있습니다. 번역된 버전이 없다면 원문이 보여질 것입니다. 항상 원문과 번역본의 내용이 일치하는지 확인하세요.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: 서버 규칙 관리
|
||||
preamble: 이 서버가 어떻게 운영되고, 중재되고, 자금을 조달하는지 등에 관한 자세한 정보를 기입하세요.
|
||||
rules_hint: 사용자들이 준수해야 할 규칙들을 위한 전용 공간입니다.
|
||||
title: 정보
|
||||
allow_referrer_origin:
|
||||
desc: 사용자가 외부 사이트 링크를 클릭한 경우 브라우저는 마스토돈 서버 주소를 리퍼러로 보내게 됩니다. 이 서버가 개인 서버인 경우 등 사용자를 식별할 수 있는 경우 이 기능을 비활성화하세요.
|
||||
title: 외부 사이트가 이 마스토돈 서버를 트래픽 소스로 볼 수 있도록 허용
|
||||
appearance:
|
||||
preamble: 마스토돈의 웹 인터페이스를 변경
|
||||
title: 외관
|
||||
@@ -804,6 +813,7 @@ ko:
|
||||
discovery:
|
||||
follow_recommendations: 팔로우 추천
|
||||
preamble: 흥미로운 콘텐츠를 노출하는 것은 마스토돈을 알지 못할 수도 있는 신규 사용자를 유입시키는 데 중요합니다. 이 서버에서 작동하는 다양한 발견하기 기능을 제어합니다.
|
||||
privacy: 개인정보
|
||||
profile_directory: 프로필 책자
|
||||
public_timelines: 공개 타임라인
|
||||
publish_statistics: 통계 발행
|
||||
@@ -890,6 +900,8 @@ ko:
|
||||
system_checks:
|
||||
database_schema_check:
|
||||
message_html: 대기 중인 데이터베이스 마이그레이션이 있습니다. 애플리케이션이 예상대로 동작할 수 있도록 마이그레이션을 실행해 주세요
|
||||
elasticsearch_analysis_index_mismatch:
|
||||
message_html: Elasticsearch 인덱스 아날라이저 설정이 업데이트되지 않았습니다. <code>tootctl search deploy --only-mapping --only=%{value}</code> 명령을 실행해주세요
|
||||
elasticsearch_health_red:
|
||||
message_html: Elasticsearch 클러스터가 비정상(red)상태입니다. 검색 기능이 동작하지 않습니다
|
||||
elasticsearch_health_yellow:
|
||||
@@ -1814,6 +1826,10 @@ ko:
|
||||
limit: 이미 너무 많은 게시물을 고정했습니다
|
||||
ownership: 다른 사람의 게시물은 고정될 수 없습니다
|
||||
reblog: 부스트는 고정될 수 없습니다
|
||||
quote_policies:
|
||||
followers: 팔로워와 멘션된 사람들만
|
||||
nobody: 멘션된 사람들만
|
||||
public: 모두
|
||||
title: '%{name}: "%{quote}"'
|
||||
visibilities:
|
||||
direct: 다이렉트
|
||||
@@ -1867,6 +1883,9 @@ ko:
|
||||
does_not_match_previous_name: 이전 이름과 맞지 않습니다
|
||||
terms_of_service:
|
||||
title: 이용 약관
|
||||
terms_of_service_interstitial:
|
||||
review_link: 이용 약관 검토하기
|
||||
title: "%{domain} 도메인의 이용 약관에 변경 있음"
|
||||
themes:
|
||||
contrast: 마스토돈 (고대비)
|
||||
default: 마스토돈 (어두움)
|
||||
|
||||
@@ -794,17 +794,26 @@ lv:
|
||||
title: Lomas
|
||||
rules:
|
||||
add_new: Pievienot noteikumu
|
||||
add_translation: Pievienot tulkojumu
|
||||
delete: Dzēst
|
||||
description_html: Kaut arī lielākā daļa apgalvo, ka ir lasījuši un piekrīt pakalpojuma izmantošanas noteikumiem, parasti cilvēki tos neizlasa, līdz rodas sarežģījumi. <strong>Padari vienkāršāku sava servera noteikumu pārskatīšanu, sniedzot tos vienkāršā uzsvēruma punktu sarakstā!</strong> Jāmēģina atsevišķus noteikumus veidot īsus un vienkāršus, bet jāmēģina arī tos nesadalīt daudzos atsevišķos vienumos.
|
||||
edit: Labot nosacījumu
|
||||
empty: Vēl nav pievienots neviens servera noteikums.
|
||||
move_down: Pārvietot lejup
|
||||
move_up: Pārvietot augšup
|
||||
title: Servera noteikumi
|
||||
translation: Tulkojums
|
||||
translations: Tulkojumi
|
||||
translations_explanation: Jūs variet pēc izvēles pievienot noteikumu tulkojumus. Ja nav iztulkots, rādīs noklusējuma vērtību. Lūdzu, vienmēr pārliecinieties, ka sniegtais tulkojums atbilst, ir konkrētai noklusējuma vērtībai.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Pārvaldīt servera noteikumus
|
||||
preamble: Sniedz padziļinātu informāciju par to, kā serveris tiek darbināts, moderēts un finansēts.
|
||||
rules_hint: Noteikumiem, kas taviem lietotājiem ir jāievēro, ir īpaša sadaļa.
|
||||
title: Par
|
||||
allow_referrer_origin:
|
||||
desc: Kad jūsu lietotāji noklikšķina uz ārējām saitēm, viņu pārlūks var nosūtīt jūsu Mastodon servera adresi kā novirzītāju. Atspējojiet šo iestatījumu, ja tas unikāli identificē jūsu lietotājus, piemēram, ja tas ir personīgais Mastodon serveris.
|
||||
title: Ļaut ārējām vietnēm redzēt jūsu Mastodon serveri kā avotu, no kura nāk apmeklētāji
|
||||
appearance:
|
||||
preamble: Pielāgo Mastodon tīmekļa saskarni.
|
||||
title: Izskats
|
||||
@@ -824,6 +833,7 @@ lv:
|
||||
discovery:
|
||||
follow_recommendations: Sekotšanas rekomendācijas
|
||||
preamble: Interesanta satura parādīšana palīdz piesaistīt jaunus lietotājus, kuri, iespējams, nepazīst nevienu Mastodon. Kontrolē, kā tavā serverī darbojas dažādi atklāšanas līdzekļi.
|
||||
privacy: Konfidencialitāte
|
||||
profile_directory: Profila direktorija
|
||||
public_timelines: Publiskās ziņu lentas
|
||||
publish_statistics: Publicēt statistiku
|
||||
|
||||
@@ -786,6 +786,7 @@ nl:
|
||||
title: Rollen
|
||||
rules:
|
||||
add_new: Regel toevoegen
|
||||
add_translation: Vertaling toevoegen
|
||||
delete: Verwijderen
|
||||
description_html: Hoewel de meeste mensen zeggen dat ze de gebruiksvoorwaarden hebben gelezen en er mee akkoord gaan, lezen mensen deze meestal niet totdat er een probleem optreedt. <strong>Maak het eenvoudiger om de regels van deze server in één oogopslag te zien, door ze puntsgewijs in een lijst te zetten.</strong> Probeer de verschillende regels kort en simpel te houden, maar probeer ze ook niet in verschillende items onder te verdelen.
|
||||
edit: Regel bewerken
|
||||
@@ -793,6 +794,9 @@ nl:
|
||||
move_down: Omlaag verplaatsen
|
||||
move_up: Omhoog verplaatsen
|
||||
title: Serverregels
|
||||
translation: Vertaling
|
||||
translations: Vertalingen
|
||||
translations_explanation: Je kunt eventueel vertalingen toevoegen voor de regels. De standaardwaarde wordt getoond als er geen vertaalde versie beschikbaar is. Zorg er altijd voor dat aanvullende vertalingen bijgewerkt blijven.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Serverregels beheren
|
||||
|
||||
@@ -319,6 +319,7 @@ pt-PT:
|
||||
create: Criar mensagem de manutenção
|
||||
title: Nova mensagem de manutenção
|
||||
preview:
|
||||
disclaimer: Dado que os utilizadores não podem opor-se a elas, as notificações por e-mail devem ser limitadas a anúncios importantes, como notificações de violação de dados pessoais ou de encerramento de servidores.
|
||||
explanation_html: 'O e-mail será enviado para <strong>%{display_count} utilizadores</strong>. O texto seguinte será incluído na mensagem de e-mail:'
|
||||
title: Pré-visualização da mensagem de manutenção
|
||||
publish: Publicar
|
||||
@@ -479,6 +480,36 @@ pt-PT:
|
||||
new:
|
||||
title: Importar bloqueios de domínio
|
||||
no_file: Nenhum ficheiro selecionado
|
||||
fasp:
|
||||
debug:
|
||||
callbacks:
|
||||
created_at: Criado em
|
||||
delete: Eliminar
|
||||
ip: Endereço de IP
|
||||
request_body: Corpo do pedido
|
||||
title: Depurar Callbacks
|
||||
providers:
|
||||
active: Ativo
|
||||
base_url: URL base
|
||||
callback: Callback
|
||||
delete: Eliminar
|
||||
edit: Editar Fornecedor
|
||||
finish_registration: Terminar inscrição
|
||||
name: Nome
|
||||
providers: Fornecedores
|
||||
public_key_fingerprint: Impressão digital da chave pública
|
||||
registration_requested: Requerida inscrição
|
||||
registrations:
|
||||
confirm: Confirmar
|
||||
description: Recebeu uma inscrição de um FASP. Rejeite-a se não foi você que a iniciou. Se foi você que a iniciou, compare cuidadosamente o nome e a impressão digital da chave antes de confirmar a inscrição.
|
||||
reject: Rejeitar
|
||||
title: Confirmar Inscrição no FASP
|
||||
save: Guardar
|
||||
select_capabilities: Selecionar Capacidades
|
||||
sign_in: Iniciar Sessão
|
||||
status: Estado
|
||||
title: Fornecedores de Serviços Auxiliares no Fediverso
|
||||
title: FASP
|
||||
follow_recommendations:
|
||||
description_html: "<strong>Recomendações de quem seguir ajudam novos utilizadores a encontrar conteúdo interessante rapidamente.</strong>. Quando um utilizador não interage com outros o suficiente para formar recomendações personalizadas, estas contas são recomendadas. Elas são recalculadas diariamente a partir de uma mistura de contas com mais atividade recente e maior número de seguidores locais para um determinado idioma."
|
||||
language: Para o idioma
|
||||
@@ -755,17 +786,26 @@ pt-PT:
|
||||
title: Funções
|
||||
rules:
|
||||
add_new: Adicionar regra
|
||||
add_translation: Adicionar tradução
|
||||
delete: Eliminar
|
||||
description_html: Embora a maioria afirme ter lido e concordado com os termos de serviço, geralmente as pessoas só os leem depois de lhes surgir um problema. <strong>Torne fácil a leitura rápida das regras do seu servidor, apresentando-as numa lista de tópicos.</strong> Tente que cada regra seja sucinta e simples, mas tente também não dividi-las num número excessivo de tópicos separados.
|
||||
edit: Editar regra
|
||||
empty: Ainda não foi definida nenhuma regra do servidor.
|
||||
move_down: Mover para baixo
|
||||
move_up: Mover para cima
|
||||
title: Regras do servidor
|
||||
translation: Tradução
|
||||
translations: Traduções
|
||||
translations_explanation: Opcionalmente, pode adicionar traduções para as regras do servidor. A versão predefinida será apresentada se não estiver disponível uma versão traduzida. Certifique-se sempre de que qualquer tradução fornecida está em sincronia com a versão predefinida.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Gerir regras do servidor
|
||||
preamble: Forneça informações aprofundadas sobre como o servidor é operado, moderado, financiado.
|
||||
rules_hint: Existe uma área dedicada às regras a que os teus utilizadores devem aderir.
|
||||
title: Sobre
|
||||
allow_referrer_origin:
|
||||
desc: Quando os seus utilizadores clicam em links para sites externos, o navegador deles pode enviar o endereço do seu servidor Mastodon como referenciador. Desative esta opção se isso identificar inequivocamente os seus utilizadores, por exemplo, se este for um servidor Mastodon pessoal.
|
||||
title: Permitir que sites externos vejam o seu servidor Mastodon como uma fonte de tráfego
|
||||
appearance:
|
||||
preamble: Personaliza a interface web do Mastodon.
|
||||
title: Aparência
|
||||
@@ -785,6 +825,7 @@ pt-PT:
|
||||
discovery:
|
||||
follow_recommendations: Recomendações de contas
|
||||
preamble: Revelar conteúdos interessantes é fundamental para a entrada de novos utilizadores que podem não conhecer ninguém no Mastodon. Controla como os vários recursos de descoberta funcionam no teu servidor.
|
||||
privacy: Privacidade
|
||||
profile_directory: Diretório de perfis
|
||||
public_timelines: Cronologias públicas
|
||||
publish_statistics: Publicar estatísticas
|
||||
@@ -871,6 +912,8 @@ pt-PT:
|
||||
system_checks:
|
||||
database_schema_check:
|
||||
message_html: Existem migrações de bases de dados pendentes. Execute-as para garantir que a aplicação se comporta como esperado
|
||||
elasticsearch_analysis_index_mismatch:
|
||||
message_html: As definições do analisador de índices do Elasticsearch estão desactualizadas. Por favor, execute <code>tootctl search deploy --only-mapping --only=%{value}</code>
|
||||
elasticsearch_health_red:
|
||||
message_html: O cluster elasticsearch não está de boa saúde (estado vermelho), as funcionalidades de pesquisa não estão disponíveis
|
||||
elasticsearch_health_yellow:
|
||||
@@ -1824,6 +1867,10 @@ pt-PT:
|
||||
limit: Já fixaste a quantidade máxima de publicações
|
||||
ownership: Não podem ser fixadas publicações de outras pessoas
|
||||
reblog: Não é possível fixar um impulso
|
||||
quote_policies:
|
||||
followers: Seguidores e utilizadores mencionados
|
||||
nobody: Apenas utilizadores mencionados
|
||||
public: Todos
|
||||
title: '%{name}: "%{quote}"'
|
||||
visibilities:
|
||||
direct: Direto
|
||||
@@ -1877,6 +1924,11 @@ pt-PT:
|
||||
does_not_match_previous_name: não coincide com o nome anterior
|
||||
terms_of_service:
|
||||
title: Termos de Serviço
|
||||
terms_of_service_interstitial:
|
||||
future_preamble_html: Estamos a fazer algumas alterações aos nossos termos de serviço, que entrarão em vigor a partir de <strong>%{date}</strong>. Recomendamos que reveja os termos atualizados.
|
||||
past_preamble_html: Alterámos os nossos termos de serviço desde a sua última visita. Recomendamos que reveja os termos atualizados.
|
||||
review_link: Rever termos de serviço
|
||||
title: Os termos de serviço de %{domain} estão a ser alterados
|
||||
themes:
|
||||
contrast: Mastodon (alto contraste)
|
||||
default: Mastodon (escuro)
|
||||
|
||||
@@ -226,6 +226,7 @@ ko:
|
||||
setting_boost_modal: 부스트 전 확인창을 띄웁니다
|
||||
setting_default_language: 게시물 언어
|
||||
setting_default_privacy: 게시물 프라이버시
|
||||
setting_default_quote_policy: 인용할 수 있는 사람
|
||||
setting_default_sensitive: 미디어를 언제나 민감한 콘텐츠로 설정
|
||||
setting_delete_modal: 게시물 삭제 전 확인창을 띄웁니다
|
||||
setting_disable_hover_cards: 호버시 프로필 미리보기를 비활성화
|
||||
|
||||
@@ -56,6 +56,7 @@ pt-PT:
|
||||
scopes: Quais as API a que a aplicação terá permissão para aceder. Se selecionar um âmbito de nível superior, não precisa de selecionar âmbitos individuais.
|
||||
setting_aggregate_reblogs: Não mostrar os novos impulsos para publicações que tenham sido recentemente impulsionadas (apenas afeta os impulsos recentemente recebidos)
|
||||
setting_always_send_emails: Normalmente as notificações por e-mail não serão enviadas quando estiver a utilizar ativamente o Mastodon
|
||||
setting_default_quote_policy: Os utilizadores mencionados têm sempre permissão para citar. Esta definição só terá efeito para publicações criadas com a próxima versão do Mastodon, mas pode selecionar a sua preferência em antecipação
|
||||
setting_default_sensitive: Os multimédia sensíveis são ocultados por predefinição e podem ser revelados com um clique/toque
|
||||
setting_display_media_default: Esconder multimédia marcada como sensível
|
||||
setting_display_media_hide_all: Esconder sempre toda a multimédia
|
||||
@@ -148,6 +149,9 @@ pt-PT:
|
||||
min_age: Não deve ter menos do que a idade mínima exigida pela legislação da sua jurisdição.
|
||||
user:
|
||||
chosen_languages: Quando selecionado, só serão mostradas nas cronologias públicas as publicações nos idiomas escolhidos
|
||||
date_of_birth:
|
||||
one: Temos de nos certificar que tem pelo menos %{count} para utilizar o Mastodon. Não vamos guardar esta informação.
|
||||
other: Temos de nos certificar que tem pelo menos %{count} para utilizar o Mastodon. Não vamos guardar esta informação.
|
||||
role: A função controla as permissões que o utilizador tem.
|
||||
user_role:
|
||||
color: Cor a ser utilizada para a função em toda a interface de utilizador, como RGB no formato hexadecimal
|
||||
@@ -228,6 +232,7 @@ pt-PT:
|
||||
setting_boost_modal: Mostrar caixa de diálogo de confirmação antes de impulsionar
|
||||
setting_default_language: Idioma de publicação
|
||||
setting_default_privacy: Privacidade da publicação
|
||||
setting_default_quote_policy: Quem pode citar
|
||||
setting_default_sensitive: Marcar sempre os multimédia como sensíveis
|
||||
setting_delete_modal: Solicitar confirmação antes de eliminar uma publicação
|
||||
setting_disable_hover_cards: Desativar visualização de perfil ao passar o cursor
|
||||
|
||||
@@ -779,11 +779,17 @@ sq:
|
||||
title: Role
|
||||
rules:
|
||||
add_new: Shtoni rregull
|
||||
add_translation: Shtoni përkthim
|
||||
delete: Fshije
|
||||
description_html: Edhe pse shumica pretendon se kanë lexuar dhe pajtohen me kushtet e shërbimit, zakonisht njerëzit nuk e lexojnë nga fillimi në fund, deri kur del një problem. <strong>Bëjeni më të lehtë parjen e rregullave të shërbyesit tuaj me një vështrim, duke i dhënë në një listë të thjeshtë me pika.</strong> Përpiquni që rregullat të jenë secili të shkurtër dhe të thjeshtë, por as mos u përpiqni t’i ndani në shumë zëra të veçantë.
|
||||
edit: Përpunoni rregull
|
||||
empty: S’janë përcaktuar ende rregulla shërbyesi.
|
||||
move_down: Lëvize poshtë
|
||||
move_up: Lëvize sipër
|
||||
title: Rregulla shërbyesi
|
||||
translation: Përkthim
|
||||
translations: Përkthime
|
||||
translations_explanation: Në daçi, mund të shtoni përkthime për rregullat. Nëse s’ka version të përkthyer, do të shfaqet vlera parazgjedhje. Ju lutemi, garantoni përherë se përkthimi i dhënë është i njëkohësuar me vlerën parazgjedhje.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Administroni rregulla shërbyesi
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
tr:
|
||||
about:
|
||||
about_mastodon_html: Mastodon <em>ücretsiz ve açık kaynaklı</em> bir sosyal ağdır. <em>Merkezi olmayan</em> yapısı sayesinde diğer ticari sosyal platformların aksine iletişimininizin tek bir firmada tutulmasının/yönetilmesinin önüne geçer. Güvendiğiniz bir sunucuyu seçerek oradaki kişilerle etkileşimde bulunabilirsiniz. Herkes kendi Mastodon sunucusunu kurabilir ve sorunsuz bir şekilde Mastodon <em>sosyal ağına</em> dahil edebilir!
|
||||
about_mastodon_html: Jetub Max<em>ücretsiz ve açık kaynaklı</em> bir sosyal ağdır. <em>Merkezi olmayan</em> yapısı sayesinde diğer ticari sosyal platformların aksine iletişimininizin tek bir firmada tutulmasının/yönetilmesinin önüne geçer. Güvendiğiniz bir sunucuyu seçerek oradaki kişilerle etkileşimde bulunabilirsiniz. Herkes kendi Jetub Max sunucusunu kurabilir ve sorunsuz bir şekilde Jetub Max<em>sosyal ağına</em> dahil edebilir!
|
||||
contact_missing: Ayarlanmadı
|
||||
contact_unavailable: Bulunamadı
|
||||
hosted_on: Mastodon %{domain} üzerinde barındırılıyor
|
||||
@@ -786,6 +786,7 @@ tr:
|
||||
title: Roller
|
||||
rules:
|
||||
add_new: Kural ekle
|
||||
add_translation: Çeviri ekle
|
||||
delete: Sil
|
||||
description_html: Her ne kadar çoğu hizmet kullanım şartlarını okuyup kabul ettiğini söylese de, insanlar onu ancak bir sorun çıktığında gözden geçiriyorlar. <strong>Sunucunuzun kurallarını bir bakışta kolayca görülecek şekilde düz bir madde listesi şeklinde sunun</strong>. Tekil kuralları kısa ve yalın tutmaya çalışan ama onları çok sayıda maddeye bölmemeye de çalışın.
|
||||
edit: Kuralı düzenle
|
||||
@@ -793,6 +794,9 @@ tr:
|
||||
move_down: Aşağı taşı
|
||||
move_up: Yukarı taşı
|
||||
title: Sunucu kuralları
|
||||
translation: Çeviri
|
||||
translations: Çeviriler
|
||||
translations_explanation: İsteğe bağlı olarak kurallar için çeviriler ekleyebilirsiniz. Çevrilmiş bir sürüm mevcut değilse varsayılan değer gösterilecektir. Lütfen her zaman sağlanan çevirinin varsayılan değerle senkronize olduğundan emin olun.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Sunucu kurallarını yönet
|
||||
|
||||
@@ -772,6 +772,7 @@ zh-TW:
|
||||
title: 角色
|
||||
rules:
|
||||
add_new: 新增規則
|
||||
add_translation: 新增翻譯
|
||||
delete: 刪除
|
||||
description_html: 雖然大多數人皆宣稱已閱讀並同意服務條款,通常直到某些問題發生時人們從未讀過。<strong>以透過提供條列式規則的方式使您的伺服器規則可以一目了然。</strong>試著維持各項條款簡短而明瞭,但也試著不要將條款切割為許多分開的項目。
|
||||
edit: 編輯規則
|
||||
@@ -779,6 +780,9 @@ zh-TW:
|
||||
move_down: 向下移
|
||||
move_up: 向上移
|
||||
title: 伺服器規則
|
||||
translation: 翻譯
|
||||
translations: 翻譯
|
||||
translations_explanation: 您可以選擇性地替規則添加翻譯。若沒有翻譯版本,預設值將被顯示。請確保任何提供之翻譯與預設值內容同步。
|
||||
settings:
|
||||
about:
|
||||
manage_rules: 管理伺服器規則
|
||||
|
||||
@@ -31,6 +31,7 @@ class Sanitize
|
||||
next true if /^(h|p|u|dt|e)-/.match?(e) # microformats classes
|
||||
next true if /^(mention|hashtag)$/.match?(e) # semantic classes
|
||||
next true if /^(ellipsis|invisible)$/.match?(e) # link formatting classes
|
||||
next true if e == 'quote-inline'
|
||||
end
|
||||
|
||||
node['class'] = class_list.join(' ')
|
||||
@@ -122,6 +123,7 @@ class Sanitize
|
||||
'blockquote' => %w(cite),
|
||||
'ol' => %w(start reversed),
|
||||
'li' => %w(value),
|
||||
'p' => %w(class),
|
||||
},
|
||||
|
||||
add_attributes: {
|
||||
|
||||
@@ -351,5 +351,42 @@ namespace :dev do
|
||||
display_name: 'Mastodon test/showcase account',
|
||||
note: 'Test account to showcase many Mastodon features. Most of its posts are public, but some are private!'
|
||||
)
|
||||
|
||||
remote_quote = Status.create_with(
|
||||
text: <<~HTML,
|
||||
<p>This is a self-quote of a remote formatted post</p>
|
||||
<p class="quote-inline">RE: <a href="https://example.org/foo/bar/baz">https://example.org/foo/bar/baz</a></p>
|
||||
HTML
|
||||
account: remote_account,
|
||||
uri: 'https://example.org/foo/bar/quote',
|
||||
url: 'https://example.org/foo/bar/quote'
|
||||
).find_or_create_by!(id: 10_000_023)
|
||||
Quote.create_with(
|
||||
status: remote_quote,
|
||||
quoted_status: remote_formatted_post,
|
||||
state: :accepted
|
||||
).find_or_create_by!(id: 10_000_010)
|
||||
Status.create_with(
|
||||
account: showcase_account,
|
||||
reblog: remote_quote
|
||||
).find_or_create_by!(id: 10_000_024)
|
||||
|
||||
media_attachment = MediaAttachment.create_with(
|
||||
account: showcase_account,
|
||||
file: File.open('spec/fixtures/files/attachment.jpg')
|
||||
).find_or_create_by!(id: 10_000_010)
|
||||
quote_post_with_media = Status.create_with(
|
||||
text: "This is a status with a picture and tags which also quotes a status with a picture.\n\n#Mastodon #Test",
|
||||
ordered_media_attachment_ids: [media_attachment.id],
|
||||
account: showcase_account,
|
||||
visibility: :public
|
||||
).find_or_create_by!(id: 10_000_025)
|
||||
media_attachment.update(status_id: quote_post_with_media.id)
|
||||
ProcessHashtagsService.new.call(quote_post_with_media)
|
||||
Quote.create_with(
|
||||
status: quote_post_with_media,
|
||||
quoted_status: status_with_media,
|
||||
state: :accepted
|
||||
).find_or_create_by!(id: 10_000_011)
|
||||
end
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user