Merge remote-tracking branch 'parent/main' into upstream-20240128

This commit is contained in:
KMY 2025-01-31 08:52:03 +09:00
commit bd5b417d2b
107 changed files with 795 additions and 246 deletions

View file

@ -9,6 +9,7 @@
# See: https://docs.docker.com/build/building/multi-platform/ # See: https://docs.docker.com/build/building/multi-platform/
ARG TARGETPLATFORM=${TARGETPLATFORM} ARG TARGETPLATFORM=${TARGETPLATFORM}
ARG BUILDPLATFORM=${BUILDPLATFORM} ARG BUILDPLATFORM=${BUILDPLATFORM}
ARG BASE_REGISTRY="docker.io"
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"] # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
# renovate: datasource=docker depName=docker.io/ruby # renovate: datasource=docker depName=docker.io/ruby
@ -19,9 +20,9 @@ ARG NODE_MAJOR_VERSION="22"
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"] # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
ARG DEBIAN_VERSION="bookworm" ARG DEBIAN_VERSION="bookworm"
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim) # Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm) # Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm)
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
# Example: v4.3.0-nightly.2023.11.09+pr-123456 # Example: v4.3.0-nightly.2023.11.09+pr-123456

View file

@ -370,7 +370,7 @@ GEM
marcel (~> 1.0.1) marcel (~> 1.0.1)
mime-types mime-types
terrapin (>= 0.6.0, < 2.0) terrapin (>= 0.6.0, < 2.0)
language_server-protocol (3.17.0.3) language_server-protocol (3.17.0.4)
launchy (3.0.1) launchy (3.0.1)
addressable (~> 2.8) addressable (~> 2.8)
childprocess (~> 5.0) childprocess (~> 5.0)
@ -530,7 +530,7 @@ GEM
opentelemetry-instrumentation-rack (0.26.0) opentelemetry-instrumentation-rack (0.26.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.23.0) opentelemetry-instrumentation-base (~> 0.23.0)
opentelemetry-instrumentation-rails (0.35.0) opentelemetry-instrumentation-rails (0.35.1)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-action_mailer (~> 0.4.0) opentelemetry-instrumentation-action_mailer (~> 0.4.0)
opentelemetry-instrumentation-action_pack (~> 0.11.0) opentelemetry-instrumentation-action_pack (~> 0.11.0)
@ -716,7 +716,7 @@ GEM
rspec-mocks (~> 3.0) rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8) sidekiq (>= 5, < 8)
rspec-support (3.13.2) rspec-support (3.13.2)
rubocop (1.70.0) rubocop (1.71.0)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (>= 3.17.0)
parallel (~> 1.10) parallel (~> 1.10)
@ -726,14 +726,14 @@ GEM
rubocop-ast (>= 1.36.2, < 2.0) rubocop-ast (>= 1.36.2, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.37.0) rubocop-ast (1.38.0)
parser (>= 3.3.1.0) parser (>= 3.3.1.0)
rubocop-capybara (2.21.0) rubocop-capybara (2.21.0)
rubocop (~> 1.41) rubocop (~> 1.41)
rubocop-performance (1.23.1) rubocop-performance (1.23.1)
rubocop (>= 1.48.1, < 2.0) rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rails (2.28.0) rubocop-rails (2.29.1)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.52.0, < 2.0) rubocop (>= 1.52.0, < 2.0)

View file

@ -117,7 +117,7 @@ module SignatureVerification
def verify_signature_strength! def verify_signature_strength!
raise SignatureVerificationError, 'Mastodon requires the Date header or (created) pseudo-header to be signed' unless signed_headers.include?('date') || signed_headers.include?('(created)') raise SignatureVerificationError, 'Mastodon requires the Date header or (created) pseudo-header to be signed' unless signed_headers.include?('date') || signed_headers.include?('(created)')
raise SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(Request::REQUEST_TARGET) || signed_headers.include?('digest') raise SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(HttpSignatureDraft::REQUEST_TARGET) || signed_headers.include?('digest')
raise SignatureVerificationError, 'Mastodon requires the Host header to be signed when doing a GET request' if request.get? && !signed_headers.include?('host') raise SignatureVerificationError, 'Mastodon requires the Host header to be signed when doing a GET request' if request.get? && !signed_headers.include?('host')
raise SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if request.post? && !signed_headers.include?('digest') raise SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if request.post? && !signed_headers.include?('digest')
end end
@ -155,14 +155,14 @@ module SignatureVerification
def build_signed_string(include_query_string: true) def build_signed_string(include_query_string: true)
signed_headers.map do |signed_header| signed_headers.map do |signed_header|
case signed_header case signed_header
when Request::REQUEST_TARGET when HttpSignatureDraft::REQUEST_TARGET
if include_query_string if include_query_string
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.original_fullpath}" "#{HttpSignatureDraft::REQUEST_TARGET}: #{request.method.downcase} #{request.original_fullpath}"
else else
# Current versions of Mastodon incorrectly omit the query string from the (request-target) pseudo-header. # Current versions of Mastodon incorrectly omit the query string from the (request-target) pseudo-header.
# Therefore, temporarily support such incorrect signatures for compatibility. # Therefore, temporarily support such incorrect signatures for compatibility.
# TODO: remove eventually some time after release of the fixed version # TODO: remove eventually some time after release of the fixed version
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}" "#{HttpSignatureDraft::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
end end
when '(created)' when '(created)'
raise SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019' raise SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019'

View file

@ -0,0 +1,55 @@
import { useRef, useCallback } from 'react';
type Position = [number, number];
export const useSelectableClick = (
onClick: React.MouseEventHandler,
maxDelta = 5,
) => {
const clickPositionRef = useRef<Position | null>(null);
const handleMouseDown = useCallback((e: React.MouseEvent) => {
clickPositionRef.current = [e.clientX, e.clientY];
}, []);
const handleMouseUp = useCallback(
(e: React.MouseEvent) => {
if (!clickPositionRef.current) {
return;
}
const [startX, startY] = clickPositionRef.current;
const [deltaX, deltaY] = [
Math.abs(e.clientX - startX),
Math.abs(e.clientY - startY),
];
let element: EventTarget | null = e.target;
while (element && element instanceof HTMLElement) {
if (
element.localName === 'button' ||
element.localName === 'a' ||
element.localName === 'label'
) {
return;
}
element = element.parentNode;
}
if (
deltaX + deltaY < maxDelta &&
(e.button === 0 || e.button === 1) &&
e.detail >= 1
) {
onClick(e);
}
clickPositionRef.current = null;
},
[maxDelta, onClick],
);
return [handleMouseDown, handleMouseUp] as const;
};

View file

@ -1,4 +1,4 @@
import { useState, useCallback, useRef } from 'react'; import { useState, useCallback, useRef, useId } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@ -8,12 +8,15 @@ import type {
UsePopperOptions, UsePopperOptions,
} from 'react-overlays/esm/usePopper'; } from 'react-overlays/esm/usePopper';
import { useSelectableClick } from '@/hooks/useSelectableClick';
const offset = [0, 4] as OffsetValue; const offset = [0, 4] as OffsetValue;
const popperConfig = { strategy: 'fixed' } as UsePopperOptions; const popperConfig = { strategy: 'fixed' } as UsePopperOptions;
export const AltTextBadge: React.FC<{ export const AltTextBadge: React.FC<{
description: string; description: string;
}> = ({ description }) => { }> = ({ description }) => {
const accessibilityId = useId();
const anchorRef = useRef<HTMLButtonElement>(null); const anchorRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@ -25,12 +28,16 @@ export const AltTextBadge: React.FC<{
setOpen(false); setOpen(false);
}, [setOpen]); }, [setOpen]);
const [handleMouseDown, handleMouseUp] = useSelectableClick(handleClose);
return ( return (
<> <>
<button <button
ref={anchorRef} ref={anchorRef}
className='media-gallery__alt__label' className='media-gallery__alt__label'
onClick={handleClick} onClick={handleClick}
aria-expanded={open}
aria-controls={accessibilityId}
> >
ALT ALT
</button> </button>
@ -47,9 +54,12 @@ export const AltTextBadge: React.FC<{
> >
{({ props }) => ( {({ props }) => (
<div {...props} className='hover-card-controller'> <div {...props} className='hover-card-controller'>
<div <div // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
className='media-gallery__alt__popover dropdown-animation' className='media-gallery__alt__popover dropdown-animation'
role='tooltip' role='region'
id={accessibilityId}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
> >
<h4> <h4>
<FormattedMessage <FormattedMessage

View file

@ -1,9 +1,10 @@
import { useCallback } from 'react'; import { useCallback, useState } from 'react';
import { useIntl, defineMessages } from 'react-intl'; import { useIntl, defineMessages } from 'react-intl';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
const messages = defineMessages({ const messages = defineMessages({
load_more: { id: 'status.load_more', defaultMessage: 'Load more' }, load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
@ -17,10 +18,12 @@ interface Props<T> {
export const LoadGap = <T,>({ disabled, param, onClick }: Props<T>) => { export const LoadGap = <T,>({ disabled, param, onClick }: Props<T>) => {
const intl = useIntl(); const intl = useIntl();
const [loading, setLoading] = useState(false);
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
setLoading(true);
onClick(param); onClick(param);
}, [param, onClick]); }, [setLoading, param, onClick]);
return ( return (
<button <button
@ -28,8 +31,13 @@ export const LoadGap = <T,>({ disabled, param, onClick }: Props<T>) => {
disabled={disabled} disabled={disabled}
onClick={handleClick} onClick={handleClick}
aria-label={intl.formatMessage(messages.load_more)} aria-label={intl.formatMessage(messages.load_more)}
title={intl.formatMessage(messages.load_more)}
> >
{loading ? (
<LoadingIndicator />
) : (
<Icon id='ellipsis-h' icon={MoreHorizIcon} /> <Icon id='ellipsis-h' icon={MoreHorizIcon} />
)}
</button> </button>
); );
}; };

View file

@ -342,7 +342,7 @@ class Status extends ImmutablePureComponent {
const { onToggleHidden } = this.props; const { onToggleHidden } = this.props;
const status = this._properStatus(); const status = this._properStatus();
if (status.get('matched_filters')) { if (this.props.status.get('matched_filters')) {
const expandedBecauseOfCW = !status.get('hidden') || status.get('spoiler_text').length === 0; const expandedBecauseOfCW = !status.get('hidden') || status.get('spoiler_text').length === 0;
const expandedBecauseOfFilter = this.state.showDespiteFilter; const expandedBecauseOfFilter = this.state.showDespiteFilter;

View file

@ -1,4 +1,4 @@
import { useState, useRef, useCallback } from 'react'; import { useState, useRef, useCallback, useId } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@ -16,6 +16,7 @@ export const DomainPill: React.FC<{
username: string; username: string;
isSelf: boolean; isSelf: boolean;
}> = ({ domain, username, isSelf }) => { }> = ({ domain, username, isSelf }) => {
const accessibilityId = useId();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
const triggerRef = useRef(null); const triggerRef = useRef(null);
@ -34,6 +35,8 @@ export const DomainPill: React.FC<{
className={classNames('account__domain-pill', { active: open })} className={classNames('account__domain-pill', { active: open })}
ref={triggerRef} ref={triggerRef}
onClick={handleClick} onClick={handleClick}
aria-expanded={open}
aria-controls={accessibilityId}
> >
{domain} {domain}
</button> </button>
@ -48,6 +51,8 @@ export const DomainPill: React.FC<{
{({ props }) => ( {({ props }) => (
<div <div
{...props} {...props}
role='region'
id={accessibilityId}
className='account__domain-pill__popout dropdown-animation' className='account__domain-pill__popout dropdown-animation'
> >
<div className='account__domain-pill__popout__header'> <div className='account__domain-pill__popout__header'>

View file

@ -6,6 +6,7 @@ import classNames from 'classnames';
import Overlay from 'react-overlays/Overlay'; import Overlay from 'react-overlays/Overlay';
import { useSelectableClick } from '@/hooks/useSelectableClick';
import QuestionMarkIcon from '@/material-icons/400-24px/question_mark.svg?react'; import QuestionMarkIcon from '@/material-icons/400-24px/question_mark.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
@ -23,6 +24,8 @@ export const InfoButton: React.FC = () => {
setOpen(!open); setOpen(!open);
}, [open, setOpen]); }, [open, setOpen]);
const [handleMouseDown, handleMouseUp] = useSelectableClick(handleClick);
return ( return (
<> <>
<button <button
@ -46,10 +49,13 @@ export const InfoButton: React.FC = () => {
target={triggerRef} target={triggerRef}
> >
{({ props }) => ( {({ props }) => (
<div <div // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
{...props} {...props}
className='dialog-modal__popout prose dropdown-animation' className='dialog-modal__popout prose dropdown-animation'
role='region'
id={accessibilityId} id={accessibilityId}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
> >
<FormattedMessage <FormattedMessage
id='info_button.what_is_alt_text' id='info_button.what_is_alt_text'

View file

@ -10,6 +10,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { length } from 'stringz'; import { length } from 'stringz';
import { missingAltTextModal } from 'mastodon/initial_state';
import AutosuggestInput from '../../../components/autosuggest_input'; import AutosuggestInput from '../../../components/autosuggest_input';
import AutosuggestTextarea from '../../../components/autosuggest_textarea'; import AutosuggestTextarea from '../../../components/autosuggest_textarea';
import { Button } from '../../../components/button'; import { Button } from '../../../components/button';
@ -73,6 +75,7 @@ class ComposeForm extends ImmutablePureComponent {
autoFocus: PropTypes.bool, autoFocus: PropTypes.bool,
withoutNavigation: PropTypes.bool, withoutNavigation: PropTypes.bool,
anyMedia: PropTypes.bool, anyMedia: PropTypes.bool,
missingAltText: PropTypes.bool,
isInReply: PropTypes.bool, isInReply: PropTypes.bool,
singleColumn: PropTypes.bool, singleColumn: PropTypes.bool,
lang: PropTypes.string, lang: PropTypes.string,
@ -126,7 +129,7 @@ class ComposeForm extends ImmutablePureComponent {
return; return;
} }
this.props.onSubmit(); this.props.onSubmit(missingAltTextModal && this.props.missingAltText);
if (e) { if (e) {
e.preventDefault(); e.preventDefault();

View file

@ -11,7 +11,9 @@ import {
insertExpirationCompose, insertExpirationCompose,
insertFeaturedTagCompose, insertFeaturedTagCompose,
uploadCompose, uploadCompose,
} from '../../../actions/compose'; } from 'mastodon/actions/compose';
import { openModal } from 'mastodon/actions/modal';
import ComposeForm from '../components/compose_form'; import ComposeForm from '../components/compose_form';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -29,6 +31,7 @@ const mapStateToProps = state => ({
isChangingUpload: state.getIn(['compose', 'is_changing_upload']), isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
isUploading: state.getIn(['compose', 'is_uploading']), isUploading: state.getIn(['compose', 'is_uploading']),
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0, anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
missingAltText: state.getIn(['compose', 'media_attachments']).some(media => ['image', 'gifv'].includes(media.get('type')) && (media.get('description') ?? '').length === 0),
isInReply: state.getIn(['compose', 'in_reply_to']) !== null, isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
lang: state.getIn(['compose', 'language']), lang: state.getIn(['compose', 'language']),
circleId: state.getIn(['compose', 'circle_id']), circleId: state.getIn(['compose', 'circle_id']),
@ -41,8 +44,15 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(changeCompose(text)); dispatch(changeCompose(text));
}, },
onSubmit () { onSubmit (missingAltText) {
if (missingAltText) {
dispatch(openModal({
modalType: 'CONFIRM_MISSING_ALT_TEXT',
modalProps: {},
}));
} else {
dispatch(submitCompose()); dispatch(submitCompose());
}
}, },
onClearSuggestions () { onClearSuggestions () {

View file

@ -73,4 +73,4 @@ const guessLanguage = (text) => {
export const debouncedGuess = debounce((text, setGuess) => { export const debouncedGuess = debounce((text, setGuess) => {
setGuess(guessLanguage(text)); setGuess(guessLanguage(text));
}, 500, { leading: true, trailing: true }); }, 500, { maxWait: 1500, leading: true, trailing: true });

View file

@ -56,14 +56,6 @@ export const ConfirmationModal: React.FC<
<div className='safety-action-modal__bottom'> <div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'> <div className='safety-action-modal__actions'>
{secondary && (
<>
<Button onClick={handleSecondary}>{secondary}</Button>
<div className='spacer' />
</>
)}
<button onClick={handleCancel} className='link-button'> <button onClick={handleCancel} className='link-button'>
<FormattedMessage <FormattedMessage
id='confirmation_modal.cancel' id='confirmation_modal.cancel'
@ -71,6 +63,15 @@ export const ConfirmationModal: React.FC<
/> />
</button> </button>
{secondary && (
<>
<div className='spacer' />
<button onClick={handleSecondary} className='link-button'>
{secondary}
</button>
</>
)}
{/* eslint-disable-next-line jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */} {/* eslint-disable-next-line jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
<Button onClick={handleClick} autoFocus> <Button onClick={handleClick} autoFocus>
{confirm} {confirm}

View file

@ -10,3 +10,4 @@ export { ConfirmUnfollowModal } from './unfollow';
export { ConfirmClearNotificationsModal } from './clear_notifications'; export { ConfirmClearNotificationsModal } from './clear_notifications';
export { ConfirmLogOutModal } from './log_out'; export { ConfirmLogOutModal } from './log_out';
export { ConfirmFollowToListModal } from './follow_to_list'; export { ConfirmFollowToListModal } from './follow_to_list';
export { ConfirmMissingAltTextModal } from './missing_alt_text';

View file

@ -0,0 +1,81 @@
import { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { submitCompose } from 'mastodon/actions/compose';
import { openModal } from 'mastodon/actions/modal';
import type { MediaAttachment } from 'mastodon/models/media_attachment';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import type { BaseConfirmationModalProps } from './confirmation_modal';
import { ConfirmationModal } from './confirmation_modal';
const messages = defineMessages({
title: {
id: 'confirmations.missing_alt_text.title',
defaultMessage: 'Add alt text?',
},
confirm: {
id: 'confirmations.missing_alt_text.confirm',
defaultMessage: 'Add alt text',
},
message: {
id: 'confirmations.missing_alt_text.message',
defaultMessage:
'Your post contains media without alt text. Adding descriptions helps make your content accessible to more people.',
},
secondary: {
id: 'confirmations.missing_alt_text.secondary',
defaultMessage: 'Post anyway',
},
});
export const ConfirmMissingAltTextModal: React.FC<
BaseConfirmationModalProps
> = ({ onClose }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const mediaId = useAppSelector(
(state) =>
(
(state.compose as ImmutableMap<string, unknown>).get(
'media_attachments',
) as ImmutableList<MediaAttachment>
)
.find(
(media) =>
['image', 'gifv'].includes(media.get('type') as string) &&
((media.get('description') ?? '') as string).length === 0,
)
?.get('id') as string,
);
const handleConfirm = useCallback(() => {
dispatch(
openModal({
modalType: 'FOCAL_POINT',
modalProps: {
mediaId,
},
}),
);
}, [dispatch, mediaId]);
const handleSecondary = useCallback(() => {
dispatch(submitCompose());
}, [dispatch]);
return (
<ConfirmationModal
title={intl.formatMessage(messages.title)}
message={intl.formatMessage(messages.message)}
confirm={intl.formatMessage(messages.confirm)}
secondary={intl.formatMessage(messages.secondary)}
onConfirm={handleConfirm}
onSecondary={handleSecondary}
onClose={onClose}
/>
);
};

View file

@ -43,6 +43,7 @@ import {
ConfirmClearNotificationsModal, ConfirmClearNotificationsModal,
ConfirmLogOutModal, ConfirmLogOutModal,
ConfirmFollowToListModal, ConfirmFollowToListModal,
ConfirmMissingAltTextModal,
} from './confirmation_modals'; } from './confirmation_modals';
import ImageModal from './image_modal'; import ImageModal from './image_modal';
import MediaModal from './media_modal'; import MediaModal from './media_modal';
@ -67,6 +68,7 @@ export const MODAL_COMPONENTS = {
'CONFIRM_CLEAR_NOTIFICATIONS': () => Promise.resolve({ default: ConfirmClearNotificationsModal }), 'CONFIRM_CLEAR_NOTIFICATIONS': () => Promise.resolve({ default: ConfirmClearNotificationsModal }),
'CONFIRM_LOG_OUT': () => Promise.resolve({ default: ConfirmLogOutModal }), 'CONFIRM_LOG_OUT': () => Promise.resolve({ default: ConfirmLogOutModal }),
'CONFIRM_FOLLOW_TO_LIST': () => Promise.resolve({ default: ConfirmFollowToListModal }), 'CONFIRM_FOLLOW_TO_LIST': () => Promise.resolve({ default: ConfirmFollowToListModal }),
'CONFIRM_MISSING_ALT_TEXT': () => Promise.resolve({ default: ConfirmMissingAltTextModal }),
'MUTE': MuteModal, 'MUTE': MuteModal,
'BLOCK': BlockModal, 'BLOCK': BlockModal,
'DOMAIN_BLOCK': DomainBlockModal, 'DOMAIN_BLOCK': DomainBlockModal,

View file

@ -31,6 +31,7 @@
* @property {boolean} bookmark_category_needed * @property {boolean} bookmark_category_needed
* @property {boolean=} boost_modal * @property {boolean=} boost_modal
* @property {boolean=} delete_modal * @property {boolean=} delete_modal
* @property {boolean=} missing_alt_text_modal
* @property {boolean=} disable_swiping * @property {boolean=} disable_swiping
* @property {boolean=} disable_hover_cards * @property {boolean=} disable_hover_cards
* @property {string=} disabled_account_id * @property {string=} disabled_account_id
@ -130,6 +131,7 @@ export const autoPlayGif = getMeta('auto_play_gif');
export const bookmarkCategoryNeeded = getMeta('bookmark_category_needed'); export const bookmarkCategoryNeeded = getMeta('bookmark_category_needed');
export const boostModal = getMeta('boost_modal'); export const boostModal = getMeta('boost_modal');
export const deleteModal = getMeta('delete_modal'); export const deleteModal = getMeta('delete_modal');
export const missingAltTextModal = getMeta('missing_alt_text_modal');
export const disableSwiping = getMeta('disable_swiping'); export const disableSwiping = getMeta('disable_swiping');
export const disableHoverCards = getMeta('disable_hover_cards'); export const disableHoverCards = getMeta('disable_hover_cards');
export const disabledAccountId = getMeta('disabled_account_id'); export const disabledAccountId = getMeta('disabled_account_id');

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Tanca la sessió", "confirmations.logout.confirm": "Tanca la sessió",
"confirmations.logout.message": "Segur que vols tancar la sessió?", "confirmations.logout.message": "Segur que vols tancar la sessió?",
"confirmations.logout.title": "Tancar la sessió?", "confirmations.logout.title": "Tancar la sessió?",
"confirmations.missing_alt_text.confirm": "Afegiu text alternatiu",
"confirmations.missing_alt_text.message": "La vostra publicació té contingut sense text alternatiu. Afegir-hi descripcions la farà accessible a més persones.",
"confirmations.missing_alt_text.secondary": "Publica-la igualment",
"confirmations.missing_alt_text.title": "Hi voleu afegir text alternatiu?",
"confirmations.mute.confirm": "Silencia", "confirmations.mute.confirm": "Silencia",
"confirmations.redraft.confirm": "Esborra i reescriu", "confirmations.redraft.confirm": "Esborra i reescriu",
"confirmations.redraft.message": "Segur que vols eliminar aquest tut i tornar a escriure'l? Es perdran tots els impulsos i els favorits, i les respostes al tut original quedaran aïllades.", "confirmations.redraft.message": "Segur que vols eliminar aquest tut i tornar a escriure'l? Es perdran tots els impulsos i els favorits, i les respostes al tut original quedaran aïllades.",

View file

@ -86,6 +86,13 @@
"alert.unexpected.message": "Objevila se neočekávaná chyba.", "alert.unexpected.message": "Objevila se neočekávaná chyba.",
"alert.unexpected.title": "Jejda!", "alert.unexpected.title": "Jejda!",
"alt_text_badge.title": "Popisek", "alt_text_badge.title": "Popisek",
"alt_text_modal.add_alt_text": "Přidat alt text",
"alt_text_modal.add_text_from_image": "Přidat text z obrázku",
"alt_text_modal.cancel": "Zrušit",
"alt_text_modal.change_thumbnail": "Změnit miniaturu",
"alt_text_modal.describe_for_people_with_hearing_impairments": "Popište to pro osoby se sluchovým postižením…",
"alt_text_modal.describe_for_people_with_visual_impairments": "Popište to pro osoby se zrakovým postižením…",
"alt_text_modal.done": "Hotovo",
"announcement.announcement": "Oznámení", "announcement.announcement": "Oznámení",
"annual_report.summary.archetype.booster": "Lovec obsahu", "annual_report.summary.archetype.booster": "Lovec obsahu",
"annual_report.summary.archetype.lurker": "Špión", "annual_report.summary.archetype.lurker": "Špión",
@ -407,6 +414,8 @@
"ignore_notifications_modal.not_followers_title": "Ignorovat oznámení od lidí, kteří vás nesledují?", "ignore_notifications_modal.not_followers_title": "Ignorovat oznámení od lidí, kteří vás nesledují?",
"ignore_notifications_modal.not_following_title": "Ignorovat oznámení od lidí, které nesledujete?", "ignore_notifications_modal.not_following_title": "Ignorovat oznámení od lidí, které nesledujete?",
"ignore_notifications_modal.private_mentions_title": "Ignorovat oznámení z nevyžádaných soukromých zmínek?", "ignore_notifications_modal.private_mentions_title": "Ignorovat oznámení z nevyžádaných soukromých zmínek?",
"info_button.label": "Nápověda",
"info_button.what_is_alt_text": "<h1>Co je to alt text?</h1> <p>Alt text poskytuje popis obrázků pro lidi se zrakovými postižením, špatným připojením něbo těm, kteří potřebují více kontextu.</p> <p>Můžete zlepšit přístupnost a porozumění napsáním jasného, stručného a objektivního alt textu.</p> <ul> <li>Zachyťte důležité prvky</li> <li>Shrňte text v obrázku</li> <li>Použijte pravidelnou větnou skladbu</li> <li>Vyhněte se nadbytečným informacím</li> <li>U komplexních vizualizací (diagramy, mapy...) se zaměřte na trendy a klíčová zjištění</li> </ul>",
"interaction_modal.action.favourite": "Chcete-li pokračovat, musíte oblíbit z vašeho účtu.", "interaction_modal.action.favourite": "Chcete-li pokračovat, musíte oblíbit z vašeho účtu.",
"interaction_modal.action.follow": "Chcete-li pokračovat, musíte sledovat z vašeho účtu.", "interaction_modal.action.follow": "Chcete-li pokračovat, musíte sledovat z vašeho účtu.",
"interaction_modal.action.reblog": "Chcete-li pokračovat, musíte dát boost z vašeho účtu.", "interaction_modal.action.reblog": "Chcete-li pokračovat, musíte dát boost z vašeho účtu.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Log ud", "confirmations.logout.confirm": "Log ud",
"confirmations.logout.message": "Er du sikker på, at du vil logge ud?", "confirmations.logout.message": "Er du sikker på, at du vil logge ud?",
"confirmations.logout.title": "Log ud?", "confirmations.logout.title": "Log ud?",
"confirmations.missing_alt_text.confirm": "Tilføj alt-tekst",
"confirmations.missing_alt_text.message": "Indlægget indeholder medier uden alt-tekst. Tilføjelse af beskrivelser bidrager til at gøre indholdet tilgængeligt for flere brugere.",
"confirmations.missing_alt_text.secondary": "Læg op alligevel",
"confirmations.missing_alt_text.title": "Tilføj alt-tekst?",
"confirmations.mute.confirm": "Skjul (mute)", "confirmations.mute.confirm": "Skjul (mute)",
"confirmations.redraft.confirm": "Slet og omformulér", "confirmations.redraft.confirm": "Slet og omformulér",
"confirmations.redraft.message": "Sikker på, at dette indlæg skal slettes og omskrives? Favoritter og boosts går tabt, og svar til det oprindelige indlæg mister tilknytningen.", "confirmations.redraft.message": "Sikker på, at dette indlæg skal slettes og omskrives? Favoritter og boosts går tabt, og svar til det oprindelige indlæg mister tilknytningen.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Abmelden", "confirmations.logout.confirm": "Abmelden",
"confirmations.logout.message": "Möchtest du dich wirklich abmelden?", "confirmations.logout.message": "Möchtest du dich wirklich abmelden?",
"confirmations.logout.title": "Abmelden?", "confirmations.logout.title": "Abmelden?",
"confirmations.missing_alt_text.confirm": "Bildbeschreibung hinzufügen",
"confirmations.missing_alt_text.message": "Dein Beitrag enthält Medien ohne Bildbeschreibung. Mit Alt-Texten erreichst du auch Menschen mit einer Sehschwäche.",
"confirmations.missing_alt_text.secondary": "Trotzdem veröffentlichen",
"confirmations.missing_alt_text.title": "Bildbeschreibung hinzufügen?",
"confirmations.mute.confirm": "Stummschalten", "confirmations.mute.confirm": "Stummschalten",
"confirmations.redraft.confirm": "Löschen und neu erstellen", "confirmations.redraft.confirm": "Löschen und neu erstellen",
"confirmations.redraft.message": "Möchtest du diesen Beitrag wirklich löschen und neu verfassen? Alle Favoriten sowie die bisher geteilten Beiträge werden verloren gehen und Antworten auf den ursprünglichen Beitrag verlieren den Zusammenhang.", "confirmations.redraft.message": "Möchtest du diesen Beitrag wirklich löschen und neu verfassen? Alle Favoriten sowie die bisher geteilten Beiträge werden verloren gehen und Antworten auf den ursprünglichen Beitrag verlieren den Zusammenhang.",

View file

@ -357,6 +357,10 @@
"confirmations.logout.confirm": "Log out", "confirmations.logout.confirm": "Log out",
"confirmations.logout.message": "Are you sure you want to log out?", "confirmations.logout.message": "Are you sure you want to log out?",
"confirmations.logout.title": "Log out?", "confirmations.logout.title": "Log out?",
"confirmations.missing_alt_text.confirm": "Add alt text",
"confirmations.missing_alt_text.message": "Your post contains media without alt text. Adding descriptions helps make your content accessible to more people.",
"confirmations.missing_alt_text.secondary": "Post anyway",
"confirmations.missing_alt_text.title": "Add alt text?",
"confirmations.mute.confirm": "Mute", "confirmations.mute.confirm": "Mute",
"confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.confirm": "Delete & redraft",
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.", "confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Elsaluti", "confirmations.logout.confirm": "Elsaluti",
"confirmations.logout.message": "Ĉu vi certas, ke vi volas elsaluti?", "confirmations.logout.message": "Ĉu vi certas, ke vi volas elsaluti?",
"confirmations.logout.title": "Ĉu elsaluti?", "confirmations.logout.title": "Ĉu elsaluti?",
"confirmations.missing_alt_text.confirm": "Aldoni alttekston",
"confirmations.missing_alt_text.message": "Via afiŝo enhavas amaskomunikilaron sen altteksto. Aldono de priskriboj helpas fari vian enhavon alirebla por pli da homoj.",
"confirmations.missing_alt_text.secondary": "Afiŝu ĉiukaze",
"confirmations.missing_alt_text.title": "Ĉu aldoni alttekston?",
"confirmations.mute.confirm": "Silentigi", "confirmations.mute.confirm": "Silentigi",
"confirmations.redraft.confirm": "Forigi kaj reskribi", "confirmations.redraft.confirm": "Forigi kaj reskribi",
"confirmations.redraft.message": "Ĉu vi certas ke vi volas forigi tiun afiŝon kaj reskribi ĝin? Ĉiuj diskonigoj kaj stelumoj estos perditaj, kaj respondoj al la originala mesaĝo estos senparentaj.", "confirmations.redraft.message": "Ĉu vi certas ke vi volas forigi tiun afiŝon kaj reskribi ĝin? Ĉiuj diskonigoj kaj stelumoj estos perditaj, kaj respondoj al la originala mesaĝo estos senparentaj.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Cerrar sesión", "confirmations.logout.confirm": "Cerrar sesión",
"confirmations.logout.message": "¿Estás seguro que querés cerrar la sesión?", "confirmations.logout.message": "¿Estás seguro que querés cerrar la sesión?",
"confirmations.logout.title": "¿Cerrar sesión?", "confirmations.logout.title": "¿Cerrar sesión?",
"confirmations.missing_alt_text.confirm": "Añadir texto alternativo",
"confirmations.missing_alt_text.message": "Tu publicación contiene medios sin texto alternativo. Añadir descripciones ayuda a que tu contenido sea accesible para más personas.",
"confirmations.missing_alt_text.secondary": "Publicar de todos modos",
"confirmations.missing_alt_text.title": "¿Deseas añadir texto alternativo?",
"confirmations.mute.confirm": "Silenciar", "confirmations.mute.confirm": "Silenciar",
"confirmations.redraft.confirm": "Eliminar mensaje original y editarlo", "confirmations.redraft.confirm": "Eliminar mensaje original y editarlo",
"confirmations.redraft.message": "¿Estás seguro que querés eliminar este mensaje y volver a editarlo? Se perderán las veces marcadas como favorito y sus adhesiones, y las respuestas al mensaje original quedarán huérfanas.", "confirmations.redraft.message": "¿Estás seguro que querés eliminar este mensaje y volver a editarlo? Se perderán las veces marcadas como favorito y sus adhesiones, y las respuestas al mensaje original quedarán huérfanas.",

View file

@ -28,7 +28,7 @@
"account.enable_notifications": "Notificarme cuando @{name} publique algo", "account.enable_notifications": "Notificarme cuando @{name} publique algo",
"account.endorse": "Destacar en mi perfil", "account.endorse": "Destacar en mi perfil",
"account.featured_tags.last_status_at": "Última publicación el {date}", "account.featured_tags.last_status_at": "Última publicación el {date}",
"account.featured_tags.last_status_never": "No hay publicaciones", "account.featured_tags.last_status_never": "Sin publicaciones",
"account.featured_tags.title": "Etiquetas destacadas de {name}", "account.featured_tags.title": "Etiquetas destacadas de {name}",
"account.follow": "Seguir", "account.follow": "Seguir",
"account.follow_back": "Seguir también", "account.follow_back": "Seguir también",
@ -146,7 +146,7 @@
"column.about": "Acerca de", "column.about": "Acerca de",
"column.blocks": "Usuarios bloqueados", "column.blocks": "Usuarios bloqueados",
"column.bookmarks": "Marcadores", "column.bookmarks": "Marcadores",
"column.community": "Línea de tiempo local", "column.community": "Cronología local",
"column.create_list": "Crear lista", "column.create_list": "Crear lista",
"column.direct": "Menciones privadas", "column.direct": "Menciones privadas",
"column.directory": "Buscar perfiles", "column.directory": "Buscar perfiles",
@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Cerrar sesión", "confirmations.logout.confirm": "Cerrar sesión",
"confirmations.logout.message": "¿Estás seguro de que quieres cerrar la sesión?", "confirmations.logout.message": "¿Estás seguro de que quieres cerrar la sesión?",
"confirmations.logout.title": "¿Deseas cerrar sesión?", "confirmations.logout.title": "¿Deseas cerrar sesión?",
"confirmations.missing_alt_text.confirm": "Añadir texto alternativo",
"confirmations.missing_alt_text.message": "Tu publicación contiene contenido multimedia sin texto alternativo. Agregar descripciones ayuda a que tu contenido sea accesible para más personas.",
"confirmations.missing_alt_text.secondary": "Publicar de todas maneras",
"confirmations.missing_alt_text.title": "¿Añadir texto alternativo?",
"confirmations.mute.confirm": "Silenciar", "confirmations.mute.confirm": "Silenciar",
"confirmations.redraft.confirm": "Borrar y volver a borrador", "confirmations.redraft.confirm": "Borrar y volver a borrador",
"confirmations.redraft.message": "¿Estás seguro que quieres borrar esta publicación y editarla? Los favoritos e impulsos se perderán, y las respuestas a la publicación original quedarán separadas.", "confirmations.redraft.message": "¿Estás seguro que quieres borrar esta publicación y editarla? Los favoritos e impulsos se perderán, y las respuestas a la publicación original quedarán separadas.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Cerrar sesión", "confirmations.logout.confirm": "Cerrar sesión",
"confirmations.logout.message": "¿Seguro que quieres cerrar la sesión?", "confirmations.logout.message": "¿Seguro que quieres cerrar la sesión?",
"confirmations.logout.title": "¿Cerrar sesión?", "confirmations.logout.title": "¿Cerrar sesión?",
"confirmations.missing_alt_text.confirm": "Añadir texto alternativo",
"confirmations.missing_alt_text.message": "Tu publicación contiene medios sin texto alternativo. Añadir descripciones ayuda a que tu contenido sea accesible para más personas.",
"confirmations.missing_alt_text.secondary": "Publicar de todos modos",
"confirmations.missing_alt_text.title": "¿Deseas añadir texto alternativo?",
"confirmations.mute.confirm": "Silenciar", "confirmations.mute.confirm": "Silenciar",
"confirmations.redraft.confirm": "Borrar y volver a borrador", "confirmations.redraft.confirm": "Borrar y volver a borrador",
"confirmations.redraft.message": "¿Estás seguro de querer borrar esta publicación y reescribirla? Los favoritos e impulsos se perderán, y las respuestas a la publicación original quedarán sin contexto.", "confirmations.redraft.message": "¿Estás seguro de querer borrar esta publicación y reescribirla? Los favoritos e impulsos se perderán, y las respuestas a la publicación original quedarán sin contexto.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Kirjaudu ulos", "confirmations.logout.confirm": "Kirjaudu ulos",
"confirmations.logout.message": "Haluatko varmasti kirjautua ulos?", "confirmations.logout.message": "Haluatko varmasti kirjautua ulos?",
"confirmations.logout.title": "Kirjaudutaanko ulos?", "confirmations.logout.title": "Kirjaudutaanko ulos?",
"confirmations.missing_alt_text.confirm": "Lisää vaihtoehtoinen teksti",
"confirmations.missing_alt_text.message": "Julkaisussasi on mediaa ilman vaihtoehtoista tekstiä. Kuvausten lisääminen auttaa tekemään sisällöstäsi saavutettavamman useammille ihmisille.",
"confirmations.missing_alt_text.secondary": "Julkaise silti",
"confirmations.missing_alt_text.title": "Lisätäänkö vaihtoehtoinen teksti?",
"confirmations.mute.confirm": "Mykistä", "confirmations.mute.confirm": "Mykistä",
"confirmations.redraft.confirm": "Poista ja palauta muokattavaksi", "confirmations.redraft.confirm": "Poista ja palauta muokattavaksi",
"confirmations.redraft.message": "Haluatko varmasti poistaa julkaisun ja tehdä siitä luonnoksen? Suosikit ja tehostukset menetetään, ja alkuperäisen julkaisun vastaukset jäävät orvoiksi.", "confirmations.redraft.message": "Haluatko varmasti poistaa julkaisun ja tehdä siitä luonnoksen? Suosikit ja tehostukset menetetään, ja alkuperäisen julkaisun vastaukset jäävät orvoiksi.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Rita út", "confirmations.logout.confirm": "Rita út",
"confirmations.logout.message": "Ert tú vís/ur í, at tú vilt útrita teg?", "confirmations.logout.message": "Ert tú vís/ur í, at tú vilt útrita teg?",
"confirmations.logout.title": "Rita út?", "confirmations.logout.title": "Rita út?",
"confirmations.missing_alt_text.confirm": "Legg alternativan tekst afturat",
"confirmations.missing_alt_text.message": "Posturin hjá tær inniheldur miðlar uttan alternativan tekst. Leggur tú lýsingar afturat verður tilfarið hjá tær atkomuligt hjá fleiri.",
"confirmations.missing_alt_text.secondary": "Posta allíkavæl",
"confirmations.missing_alt_text.title": "Legg alternativan tekst afturat?",
"confirmations.mute.confirm": "Doyv", "confirmations.mute.confirm": "Doyv",
"confirmations.redraft.confirm": "Sletta og skriva umaftur", "confirmations.redraft.confirm": "Sletta og skriva umaftur",
"confirmations.redraft.message": "Vilt tú veruliga strika hendan postin og í staðin gera hann til eina nýggja kladdu? Yndisfrámerki og framhevjanir blíva burtur, og svar til upprunapostin missa tilknýtið.", "confirmations.redraft.message": "Vilt tú veruliga strika hendan postin og í staðin gera hann til eina nýggja kladdu? Yndisfrámerki og framhevjanir blíva burtur, og svar til upprunapostin missa tilknýtið.",

View file

@ -86,14 +86,33 @@
"alert.unexpected.message": "Der is in ûnferwachte flater bard.", "alert.unexpected.message": "Der is in ûnferwachte flater bard.",
"alert.unexpected.title": "Oepsy!", "alert.unexpected.title": "Oepsy!",
"alt_text_badge.title": "Alternative tekst", "alt_text_badge.title": "Alternative tekst",
"alt_text_modal.add_alt_text": "Alt-tekst tafoegje",
"alt_text_modal.add_text_from_image": "Tekst fan ôfbylding tafoegje",
"alt_text_modal.cancel": "Annulearje",
"alt_text_modal.change_thumbnail": "Miniatuerôfbylding wizigje",
"alt_text_modal.describe_for_people_with_hearing_impairments": "Beskriuw dit foar dôven en hurdhearrige…",
"alt_text_modal.describe_for_people_with_visual_impairments": "Beskriuw dit foar blinen en fisueel beheinde…",
"alt_text_modal.done": "Klear",
"announcement.announcement": "Oankundiging", "announcement.announcement": "Oankundiging",
"annual_report.summary.archetype.booster": "De cool-hunter", "annual_report.summary.archetype.booster": "De cool-hunter",
"annual_report.summary.archetype.lurker": "De lurker",
"annual_report.summary.archetype.oracle": "It orakel", "annual_report.summary.archetype.oracle": "It orakel",
"annual_report.summary.archetype.pollster": "De opinypeiler", "annual_report.summary.archetype.pollster": "De opinypeiler",
"annual_report.summary.archetype.replier": "De sosjale flinter", "annual_report.summary.archetype.replier": "De sosjale flinter",
"annual_report.summary.followers.followers": "folgers", "annual_report.summary.followers.followers": "folgers",
"annual_report.summary.followers.total": "totaal {count}", "annual_report.summary.followers.total": "totaal {count}",
"annual_report.summary.here_it_is": "Jo jieroersjoch foar {year}:", "annual_report.summary.here_it_is": "Jo jieroersjoch foar {year}:",
"annual_report.summary.highlighted_post.by_favourites": "berjocht mei de measte favoriten",
"annual_report.summary.highlighted_post.by_reblogs": "berjocht mei de measte boosts",
"annual_report.summary.highlighted_post.by_replies": "berjocht mei de measte reaksjes",
"annual_report.summary.highlighted_post.possessive": "{name}s",
"annual_report.summary.most_used_app.most_used_app": "meast brûkte app",
"annual_report.summary.most_used_hashtag.most_used_hashtag": "meast brûkte hashtag",
"annual_report.summary.most_used_hashtag.none": "Gjin",
"annual_report.summary.new_posts.new_posts": "nije berjochten",
"annual_report.summary.percentile.text": "<topLabel>Hjirmei hearre jo ta de top</topLabel><percentage></percentage><bottomLabel> fan {domain}.</bottomLabel>",
"annual_report.summary.percentile.we_wont_tell_bernie": "Wy sille Bernie neat fertelle.",
"annual_report.summary.thanks": "Tank dat jo part binne fan Mastodon!",
"attachments_list.unprocessed": "(net ferwurke)", "attachments_list.unprocessed": "(net ferwurke)",
"audio.hide": "Audio ferstopje", "audio.hide": "Audio ferstopje",
"block_modal.remote_users_caveat": "Wy freegje de server {domain} om jo beslút te respektearjen. It neilibben hjirfan is echter net garandearre, omdat guon servers blokkaden oars ynterpretearje kinne. Iepenbiere berjochten binne mooglik noch hieltyd sichtber foar net-oanmelde brûkers.", "block_modal.remote_users_caveat": "Wy freegje de server {domain} om jo beslút te respektearjen. It neilibben hjirfan is echter net garandearre, omdat guon servers blokkaden oars ynterpretearje kinne. Iepenbiere berjochten binne mooglik noch hieltyd sichtber foar net-oanmelde brûkers.",
@ -117,6 +136,7 @@
"bundle_column_error.routing.body": "De opfrege side kin net fûn wurde. Binne jo wis dat de URL yn de adresbalke goed is?", "bundle_column_error.routing.body": "De opfrege side kin net fûn wurde. Binne jo wis dat de URL yn de adresbalke goed is?",
"bundle_column_error.routing.title": "404", "bundle_column_error.routing.title": "404",
"bundle_modal_error.close": "Slute", "bundle_modal_error.close": "Slute",
"bundle_modal_error.message": "Der gie wat mis by it laden fan dit skerm.",
"bundle_modal_error.retry": "Opnij probearje", "bundle_modal_error.retry": "Opnij probearje",
"closed_registrations.other_server_instructions": "Omdat Mastodon desintralisearre is, kinne jo in account meitsje op in oare server en noch hieltyd ynteraksje hawwe mei dizze.", "closed_registrations.other_server_instructions": "Omdat Mastodon desintralisearre is, kinne jo in account meitsje op in oare server en noch hieltyd ynteraksje hawwe mei dizze.",
"closed_registrations_modal.description": "It oanmeitsjen fan in account op {domain} is op dit stuit net mooglik, mar hâld asjebleaft yn gedachten dat jo gjin account spesifyk op {domain} nedich hawwe om Mastodon te brûken.", "closed_registrations_modal.description": "It oanmeitsjen fan in account op {domain} is op dit stuit net mooglik, mar hâld asjebleaft yn gedachten dat jo gjin account spesifyk op {domain} nedich hawwe om Mastodon te brûken.",
@ -127,13 +147,16 @@
"column.blocks": "Blokkearre brûkers", "column.blocks": "Blokkearre brûkers",
"column.bookmarks": "Blêdwizers", "column.bookmarks": "Blêdwizers",
"column.community": "Lokale tiidline", "column.community": "Lokale tiidline",
"column.create_list": "List oanmeitsje",
"column.direct": "Priveefermeldingen", "column.direct": "Priveefermeldingen",
"column.directory": "Profilen trochsykje", "column.directory": "Profilen trochsykje",
"column.domain_blocks": "Blokkearre domeinen", "column.domain_blocks": "Blokkearre domeinen",
"column.edit_list": "List bewurkje",
"column.favourites": "Favoriten", "column.favourites": "Favoriten",
"column.firehose": "Live feeds", "column.firehose": "Live feeds",
"column.follow_requests": "Folchfersiken", "column.follow_requests": "Folchfersiken",
"column.home": "Startside", "column.home": "Startside",
"column.list_members": "Listleden beheare",
"column.lists": "Listen", "column.lists": "Listen",
"column.mutes": "Negearre brûkers", "column.mutes": "Negearre brûkers",
"column.notifications": "Meldingen", "column.notifications": "Meldingen",
@ -146,6 +169,7 @@
"column_header.pin": "Fêstsette", "column_header.pin": "Fêstsette",
"column_header.show_settings": "Ynstellingen toane", "column_header.show_settings": "Ynstellingen toane",
"column_header.unpin": "Losmeitsje", "column_header.unpin": "Losmeitsje",
"column_search.cancel": "Annulearje",
"column_subheading.settings": "Ynstellingen", "column_subheading.settings": "Ynstellingen",
"community.column_settings.local_only": "Allinnich lokaal", "community.column_settings.local_only": "Allinnich lokaal",
"community.column_settings.media_only": "Allinnich media", "community.column_settings.media_only": "Allinnich media",
@ -188,6 +212,9 @@
"confirmations.edit.confirm": "Bewurkje", "confirmations.edit.confirm": "Bewurkje",
"confirmations.edit.message": "Troch no te bewurkjen sil it berjocht dat jo no oan it skriuwen binne oerskreaun wurde. Wolle jo trochgean?", "confirmations.edit.message": "Troch no te bewurkjen sil it berjocht dat jo no oan it skriuwen binne oerskreaun wurde. Wolle jo trochgean?",
"confirmations.edit.title": "Berjocht oerskriuwe?", "confirmations.edit.title": "Berjocht oerskriuwe?",
"confirmations.follow_to_list.confirm": "Folgje en tafoegje oan de list",
"confirmations.follow_to_list.message": "Jo moatte {name} folgje om se ta te foegjen oan in list.",
"confirmations.follow_to_list.title": "Brûker folgje?",
"confirmations.logout.confirm": "Ofmelde", "confirmations.logout.confirm": "Ofmelde",
"confirmations.logout.message": "Binne jo wis dat jo ôfmelde wolle?", "confirmations.logout.message": "Binne jo wis dat jo ôfmelde wolle?",
"confirmations.logout.title": "Ofmelde?", "confirmations.logout.title": "Ofmelde?",
@ -219,6 +246,7 @@
"disabled_account_banner.text": "Jo account {disabledAccount} is op dit stuit útskeakele.", "disabled_account_banner.text": "Jo account {disabledAccount} is op dit stuit útskeakele.",
"dismissable_banner.community_timeline": "Dit binne de meast resinte iepenbiere berjochten fan accounts op {domain}.", "dismissable_banner.community_timeline": "Dit binne de meast resinte iepenbiere berjochten fan accounts op {domain}.",
"dismissable_banner.dismiss": "Slute", "dismissable_banner.dismiss": "Slute",
"dismissable_banner.explore_links": "Dizze nijsartikelen wurde hjoed de dei it meast dield op de fediverse. Nijere artikelen dyt troch mear ferskate minsken pleatst binne, wurde heger rangskikt.",
"domain_block_modal.block": "Server blokkearje", "domain_block_modal.block": "Server blokkearje",
"domain_block_modal.block_account_instead": "Yn stee hjirfan {name} blokkearje", "domain_block_modal.block_account_instead": "Yn stee hjirfan {name} blokkearje",
"domain_block_modal.they_can_interact_with_old_posts": "Minsken op dizze server kinne ynteraksje hawwe mei jo âlde berjochten.", "domain_block_modal.they_can_interact_with_old_posts": "Minsken op dizze server kinne ynteraksje hawwe mei jo âlde berjochten.",
@ -381,6 +409,9 @@
"ignore_notifications_modal.not_followers_title": "Meldingen negearje fan minsken dyt jo net folgje?", "ignore_notifications_modal.not_followers_title": "Meldingen negearje fan minsken dyt jo net folgje?",
"ignore_notifications_modal.not_following_title": "Meldingen negearje fan minsken dyt josels net folgje?", "ignore_notifications_modal.not_following_title": "Meldingen negearje fan minsken dyt josels net folgje?",
"ignore_notifications_modal.private_mentions_title": "Meldingen negearje fan net frege priveeberjochten?", "ignore_notifications_modal.private_mentions_title": "Meldingen negearje fan net frege priveeberjochten?",
"info_button.label": "Help",
"interaction_modal.go": "Gean",
"interaction_modal.no_account_yet": "Hawwe jo noch gjin account?",
"interaction_modal.on_another_server": "Op een oare server", "interaction_modal.on_another_server": "Op een oare server",
"interaction_modal.on_this_server": "Op dizze server", "interaction_modal.on_this_server": "Op dizze server",
"interaction_modal.title.favourite": "Berjocht fan {name} as favoryt markearje", "interaction_modal.title.favourite": "Berjocht fan {name} as favoryt markearje",
@ -388,6 +419,7 @@
"interaction_modal.title.reblog": "Berjocht fan {name} booste", "interaction_modal.title.reblog": "Berjocht fan {name} booste",
"interaction_modal.title.reply": "Op it berjocht fan {name} reagearje", "interaction_modal.title.reply": "Op it berjocht fan {name} reagearje",
"interaction_modal.title.vote": "Stimme yn {name}s peiling", "interaction_modal.title.vote": "Stimme yn {name}s peiling",
"interaction_modal.username_prompt": "Byg. {example}",
"intervals.full.days": "{number, plural, one {# dei} other {# dagen}} lyn", "intervals.full.days": "{number, plural, one {# dei} other {# dagen}} lyn",
"intervals.full.hours": "{number, plural, one {# oere} other {# oeren}} lyn", "intervals.full.hours": "{number, plural, one {# oere} other {# oeren}} lyn",
"intervals.full.minutes": "{number, plural, one {# minút} other {# minuten}} lyn", "intervals.full.minutes": "{number, plural, one {# minút} other {# minuten}} lyn",
@ -423,6 +455,7 @@
"keyboard_shortcuts.toggle_hidden": "Tekst efter CW-fjild ferstopje/toane", "keyboard_shortcuts.toggle_hidden": "Tekst efter CW-fjild ferstopje/toane",
"keyboard_shortcuts.toggle_sensitivity": "Media ferstopje/toane", "keyboard_shortcuts.toggle_sensitivity": "Media ferstopje/toane",
"keyboard_shortcuts.toot": "Nij berjocht skriuwe", "keyboard_shortcuts.toot": "Nij berjocht skriuwe",
"keyboard_shortcuts.translate": "om in berjocht oer te setten",
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
"keyboard_shortcuts.up": "Nei boppe yn list ferpleatse", "keyboard_shortcuts.up": "Nei boppe yn list ferpleatse",
"lightbox.close": "Slute", "lightbox.close": "Slute",
@ -435,11 +468,32 @@
"link_preview.author": "Troch {name}", "link_preview.author": "Troch {name}",
"link_preview.more_from_author": "Mear fan {name}", "link_preview.more_from_author": "Mear fan {name}",
"link_preview.shares": "{count, plural, one {{counter} berjocht} other {{counter} berjochten}}", "link_preview.shares": "{count, plural, one {{counter} berjocht} other {{counter} berjochten}}",
"lists.add_member": "Tafoegje",
"lists.add_to_list": "Oan list tafoegje",
"lists.add_to_lists": "{name} oan listen tafoegje",
"lists.create": "Oanmeitsje",
"lists.create_a_list_to_organize": "Meitsje in nije list oan om jo starttiidline te organisearjen",
"lists.create_list": "List oanmeitsje",
"lists.delete": "List fuortsmite", "lists.delete": "List fuortsmite",
"lists.done": "Klear",
"lists.edit": "List bewurkje", "lists.edit": "List bewurkje",
"lists.exclusive": "Leden op jo Startside ferstopje",
"lists.exclusive_hint": "As ien op dizze list stiet, ferstopje dizze persoan dan op jo starttiidline om foar te kommen dat harren berjochten twa kear toand wurde.",
"lists.find_users_to_add": "Fyn brûkers om ta te foegjen",
"lists.list_members": "Listleden",
"lists.list_members_count": "{count, plural, one{# lid} other{# leden}}",
"lists.list_name": "Listnamme",
"lists.new_list_name": "Nije listnamme",
"lists.no_lists_yet": "Noch gjin listen.",
"lists.no_members_yet": "Noch gjin leden.",
"lists.no_results_found": "Gjin resultaten fûn.",
"lists.remove_member": "Fuortsmite",
"lists.replies_policy.followed": "Elke folge brûker", "lists.replies_policy.followed": "Elke folge brûker",
"lists.replies_policy.list": "Leden fan de list", "lists.replies_policy.list": "Leden fan de list",
"lists.replies_policy.none": "Net ien", "lists.replies_policy.none": "Net ien",
"lists.save": "Bewarje",
"lists.search": "Sykje",
"lists.show_replies_to": "Foegje antwurden fan listleden ta oan",
"load_pending": "{count, plural, one {# nij item} other {# nije items}}", "load_pending": "{count, plural, one {# nij item} other {# nije items}}",
"loading_indicator.label": "Lade…", "loading_indicator.label": "Lade…",
"media_gallery.hide": "Ferstopje", "media_gallery.hide": "Ferstopje",
@ -488,8 +542,12 @@
"notification.admin.report_statuses_other": "{name} hat {target} rapportearre", "notification.admin.report_statuses_other": "{name} hat {target} rapportearre",
"notification.admin.sign_up": "{name} hat harren registrearre", "notification.admin.sign_up": "{name} hat harren registrearre",
"notification.admin.sign_up.name_and_others": "{name} en {count, plural, one {# oar} other {# oaren}} hawwe harren registrearre", "notification.admin.sign_up.name_and_others": "{name} en {count, plural, one {# oar} other {# oaren}} hawwe harren registrearre",
"notification.annual_report.message": "Jo {year} #Wrapstodon stiet klear! Lit de hichtepunten en memorabele mominten fan jo jier sjen op Mastodon!",
"notification.annual_report.view": "#Wrapstodon besjen",
"notification.favourite": "{name} hat jo berjocht as favoryt markearre", "notification.favourite": "{name} hat jo berjocht as favoryt markearre",
"notification.favourite.name_and_others_with_link": "{name} en <a>{count, plural, one {# oar} other {# oaren}}</a> hawwe jo berjocht as favoryt markearre", "notification.favourite.name_and_others_with_link": "{name} en <a>{count, plural, one {# oar} other {# oaren}}</a> hawwe jo berjocht as favoryt markearre",
"notification.favourite_pm": "{name} hat jo priveeberjocht as favoryt markearre",
"notification.favourite_pm.name_and_others_with_link": "{name} en <a>{count, plural, one {# oar} other {# oaren}}</a> hawwe jo priveeberjocht as favoryt markearre",
"notification.follow": "{name} folget dy", "notification.follow": "{name} folget dy",
"notification.follow.name_and_others": "{name} en <a>{count, plural, one {# oar persoan} other {# oare persoanen}}</a> folgje jo no", "notification.follow.name_and_others": "{name} en <a>{count, plural, one {# oar persoan} other {# oare persoanen}}</a> folgje jo no",
"notification.follow_request": "{name} hat dy in folchfersyk stjoerd", "notification.follow_request": "{name} hat dy in folchfersyk stjoerd",
@ -594,7 +652,11 @@
"notifications_permission_banner.enable": "Desktopmeldingen ynskeakelje", "notifications_permission_banner.enable": "Desktopmeldingen ynskeakelje",
"notifications_permission_banner.how_to_control": "Om meldingen te ûntfangen wanneart Mastodon net iepen stiet. Jo kinne krekt bepale hokker soarte fan ynteraksjes wol of gjin desktopmeldingen jouwe fia de boppesteande {icon} knop.", "notifications_permission_banner.how_to_control": "Om meldingen te ûntfangen wanneart Mastodon net iepen stiet. Jo kinne krekt bepale hokker soarte fan ynteraksjes wol of gjin desktopmeldingen jouwe fia de boppesteande {icon} knop.",
"notifications_permission_banner.title": "Mis neat", "notifications_permission_banner.title": "Mis neat",
"onboarding.follows.back": "Tebek",
"onboarding.follows.done": "Klear",
"onboarding.follows.empty": "Spitigernôch kinne op dit stuit gjin resultaten toand wurde. Jo kinne probearje te sykjen of te blêdzjen troch de ferkenningsside om minsken te finen dyt jo folgje kinne, of probearje it letter opnij.", "onboarding.follows.empty": "Spitigernôch kinne op dit stuit gjin resultaten toand wurde. Jo kinne probearje te sykjen of te blêdzjen troch de ferkenningsside om minsken te finen dyt jo folgje kinne, of probearje it letter opnij.",
"onboarding.follows.search": "Sykje",
"onboarding.follows.title": "Folgje minsken om te begjinnen",
"onboarding.profile.discoverable": "Meitsje myn profyl te finen", "onboarding.profile.discoverable": "Meitsje myn profyl te finen",
"onboarding.profile.discoverable_hint": "Wanneart jo akkoard gean mei it te finen wêzen op Mastodon, ferskine jo berjochten yn sykresultaten en kinne se trending wurde, en jo profyl kin oan oare minsken oanrekommandearre wurde wanneart se fergelykbere ynteressen hawwe.", "onboarding.profile.discoverable_hint": "Wanneart jo akkoard gean mei it te finen wêzen op Mastodon, ferskine jo berjochten yn sykresultaten en kinne se trending wurde, en jo profyl kin oan oare minsken oanrekommandearre wurde wanneart se fergelykbere ynteressen hawwe.",
"onboarding.profile.display_name": "Werjeftenamme", "onboarding.profile.display_name": "Werjeftenamme",
@ -632,6 +694,8 @@
"privacy_policy.title": "Privacybelied", "privacy_policy.title": "Privacybelied",
"recommended": "Oanrekommandearre", "recommended": "Oanrekommandearre",
"refresh": "Ferfarskje", "refresh": "Ferfarskje",
"regeneration_indicator.please_stand_by": "In amerijke.",
"regeneration_indicator.preparing_your_home_feed": "Tarieden fan jo starttiidline…",
"relative_time.days": "{number}d", "relative_time.days": "{number}d",
"relative_time.full.days": "{number, plural, one {# dei} other {# dagen}} lyn", "relative_time.full.days": "{number, plural, one {# dei} other {# dagen}} lyn",
"relative_time.full.hours": "{number, plural, one {# oere} other {# oeren}} lyn", "relative_time.full.hours": "{number, plural, one {# oere} other {# oeren}} lyn",
@ -715,8 +779,11 @@
"search_results.accounts": "Profilen", "search_results.accounts": "Profilen",
"search_results.all": "Alles", "search_results.all": "Alles",
"search_results.hashtags": "Hashtags", "search_results.hashtags": "Hashtags",
"search_results.no_results": "Gjin resultaten.",
"search_results.no_search_yet": "Probearje te sykjen nei berjochten, profilen of hashtags.",
"search_results.see_all": "Alles besjen", "search_results.see_all": "Alles besjen",
"search_results.statuses": "Berjochten", "search_results.statuses": "Berjochten",
"search_results.title": "Sykje nei {q}",
"server_banner.about_active_users": "Oantal brûkers yn de ôfrûne 30 dagen (MAU)", "server_banner.about_active_users": "Oantal brûkers yn de ôfrûne 30 dagen (MAU)",
"server_banner.active_users": "warbere brûkers", "server_banner.active_users": "warbere brûkers",
"server_banner.administered_by": "Beheard troch:", "server_banner.administered_by": "Beheard troch:",
@ -768,6 +835,7 @@
"status.reblogs.empty": "Net ien hat dit berjocht noch boost. Wanneart ien dit docht, falt dat hjir te sjen.", "status.reblogs.empty": "Net ien hat dit berjocht noch boost. Wanneart ien dit docht, falt dat hjir te sjen.",
"status.redraft": "Fuortsmite en opnij opstelle", "status.redraft": "Fuortsmite en opnij opstelle",
"status.remove_bookmark": "Blêdwizer fuortsmite", "status.remove_bookmark": "Blêdwizer fuortsmite",
"status.remove_favourite": "Ut favoriten fuortsmite",
"status.replied_in_thread": "Antwurde yn petear", "status.replied_in_thread": "Antwurde yn petear",
"status.replied_to": "Antwurde op {name}", "status.replied_to": "Antwurde op {name}",
"status.reply": "Beäntwurdzje", "status.reply": "Beäntwurdzje",
@ -789,6 +857,7 @@
"subscribed_languages.target": "Toande talen foar {target} wizigje", "subscribed_languages.target": "Toande talen foar {target} wizigje",
"tabs_bar.home": "Startside", "tabs_bar.home": "Startside",
"tabs_bar.notifications": "Meldingen", "tabs_bar.notifications": "Meldingen",
"terms_of_service.title": "Gebrûksbetingsten",
"time_remaining.days": "{number, plural, one {# dei} other {# dagen}} te gean", "time_remaining.days": "{number, plural, one {# dei} other {# dagen}} te gean",
"time_remaining.hours": "{number, plural, one {# oere} other {# oeren}} te gean", "time_remaining.hours": "{number, plural, one {# oere} other {# oeren}} te gean",
"time_remaining.minutes": "{number, plural, one {# minút} other {# minuten}} te gean", "time_remaining.minutes": "{number, plural, one {# minút} other {# minuten}} te gean",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Pechar sesión", "confirmations.logout.confirm": "Pechar sesión",
"confirmations.logout.message": "Desexas pechar a sesión?", "confirmations.logout.message": "Desexas pechar a sesión?",
"confirmations.logout.title": "Pechar sesión?", "confirmations.logout.title": "Pechar sesión?",
"confirmations.missing_alt_text.confirm": "Engadir texto descritivo",
"confirmations.missing_alt_text.message": "A publicación contén multimedia sen un texto que o describa. Ao engadir a descrición fas o contido accesible para máis persoas.",
"confirmations.missing_alt_text.secondary": "Publicar igualmente",
"confirmations.missing_alt_text.title": "Engadir texto descritivo?",
"confirmations.mute.confirm": "Acalar", "confirmations.mute.confirm": "Acalar",
"confirmations.redraft.confirm": "Eliminar e reescribir", "confirmations.redraft.confirm": "Eliminar e reescribir",
"confirmations.redraft.message": "Tes a certeza de querer eliminar esta publicación e reescribila? Perderás as promocións e favorecementos, e as respostas á publicación orixinal ficarán orfas.", "confirmations.redraft.message": "Tes a certeza de querer eliminar esta publicación e reescribila? Perderás as promocións e favorecementos, e as respostas á publicación orixinal ficarán orfas.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "התנתקות", "confirmations.logout.confirm": "התנתקות",
"confirmations.logout.message": "האם אתם בטוחים שאתם רוצים להתנתק?", "confirmations.logout.message": "האם אתם בטוחים שאתם רוצים להתנתק?",
"confirmations.logout.title": "להתנתק?", "confirmations.logout.title": "להתנתק?",
"confirmations.missing_alt_text.confirm": "הוספת מלל חלופי",
"confirmations.missing_alt_text.message": "ההודעה שלך כוללת קבצים גרפיים ללא תיאור (מלל חלופי). הוספת תיאורים עוזרת להנגיש את התוכן ליותר אנשים.",
"confirmations.missing_alt_text.secondary": "לפרסם בכל זאת",
"confirmations.missing_alt_text.title": "להוסיף מלל חלופי?",
"confirmations.mute.confirm": "להשתיק", "confirmations.mute.confirm": "להשתיק",
"confirmations.redraft.confirm": "מחיקה ועריכה מחדש", "confirmations.redraft.confirm": "מחיקה ועריכה מחדש",
"confirmations.redraft.message": "למחוק ולהתחיל טיוטה חדשה? חיבובים והדהודים יאבדו, ותגובות להודעה המקורית ישארו יתומות.", "confirmations.redraft.message": "למחוק ולהתחיל טיוטה חדשה? חיבובים והדהודים יאבדו, ותגובות להודעה המקורית ישארו יתומות.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Kijelentkezés", "confirmations.logout.confirm": "Kijelentkezés",
"confirmations.logout.message": "Biztos, hogy kijelentkezel?", "confirmations.logout.message": "Biztos, hogy kijelentkezel?",
"confirmations.logout.title": "Kijelentkezel?", "confirmations.logout.title": "Kijelentkezel?",
"confirmations.missing_alt_text.confirm": "Helyettesítő szöveg hozzáadása",
"confirmations.missing_alt_text.message": "A bejegyzés helyettesítő szöveg nélküli médiát tartalmaz. A leírások hozzáadása segít a tartalom akadálymentesebbé tételében.",
"confirmations.missing_alt_text.secondary": "Közzététel mindenképpen",
"confirmations.missing_alt_text.title": "Helyettesítő szöveg hozzáadása?",
"confirmations.mute.confirm": "Némítás", "confirmations.mute.confirm": "Némítás",
"confirmations.redraft.confirm": "Törlés és újraírás", "confirmations.redraft.confirm": "Törlés és újraírás",
"confirmations.redraft.message": "Biztos, hogy ezt a bejegyzést szeretnéd törölni és újraírni? Minden megtolást és kedvencnek jelölést elvesztesz, az eredetire adott válaszok pedig elárvulnak.", "confirmations.redraft.message": "Biztos, hogy ezt a bejegyzést szeretnéd törölni és újraírni? Minden megtolást és kedvencnek jelölést elvesztesz, az eredetire adott válaszok pedig elárvulnak.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Skrá út", "confirmations.logout.confirm": "Skrá út",
"confirmations.logout.message": "Ertu viss um að þú viljir skrá þig út?", "confirmations.logout.message": "Ertu viss um að þú viljir skrá þig út?",
"confirmations.logout.title": "Skrá út?", "confirmations.logout.title": "Skrá út?",
"confirmations.missing_alt_text.confirm": "Bæta við hjálpartexta",
"confirmations.missing_alt_text.message": "Færslan þín inniheldur myndefni án ALT-hjálpartexta. Ef þú bætir við lýsingu á myndefninu gerir það efnið þitt aðgengilegt fyrir fleira fólk.",
"confirmations.missing_alt_text.secondary": "Birta samt",
"confirmations.missing_alt_text.title": "Bæta við hjálpartexta?",
"confirmations.mute.confirm": "Þagga", "confirmations.mute.confirm": "Þagga",
"confirmations.redraft.confirm": "Eyða og endurvinna drög", "confirmations.redraft.confirm": "Eyða og endurvinna drög",
"confirmations.redraft.message": "Ertu viss um að þú viljir eyða þessari færslu og enduvinna drögin? Eftirlæti og endurbirtingar munu glatast og svör við upprunalegu færslunni munu verða munaðarlaus.", "confirmations.redraft.message": "Ertu viss um að þú viljir eyða þessari færslu og enduvinna drögin? Eftirlæti og endurbirtingar munu glatast og svör við upprunalegu færslunni munu verða munaðarlaus.",

View file

@ -414,6 +414,7 @@
"ignore_notifications_modal.not_followers_title": "나를 팔로우하지 않는 사람들의 알림을 무시할까요?", "ignore_notifications_modal.not_followers_title": "나를 팔로우하지 않는 사람들의 알림을 무시할까요?",
"ignore_notifications_modal.not_following_title": "내가 팔로우하지 않는 사람들의 알림을 무시할까요?", "ignore_notifications_modal.not_following_title": "내가 팔로우하지 않는 사람들의 알림을 무시할까요?",
"ignore_notifications_modal.private_mentions_title": "요청하지 않은 개인 멘션 알림을 무시할까요?", "ignore_notifications_modal.private_mentions_title": "요청하지 않은 개인 멘션 알림을 무시할까요?",
"info_button.label": "도움말",
"interaction_modal.action.favourite": "계속하려면 내 계정으로 즐겨찾기해야 합니다.", "interaction_modal.action.favourite": "계속하려면 내 계정으로 즐겨찾기해야 합니다.",
"interaction_modal.action.follow": "계속하려면 내 계정으로 팔로우해야 합니다.", "interaction_modal.action.follow": "계속하려면 내 계정으로 팔로우해야 합니다.",
"interaction_modal.action.reblog": "계속하려면 내 계정으로 리블로그해야 합니다.", "interaction_modal.action.reblog": "계속하려면 내 계정으로 리블로그해야 합니다.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Uitloggen", "confirmations.logout.confirm": "Uitloggen",
"confirmations.logout.message": "Weet je zeker dat je wilt uitloggen?", "confirmations.logout.message": "Weet je zeker dat je wilt uitloggen?",
"confirmations.logout.title": "Uitloggen?", "confirmations.logout.title": "Uitloggen?",
"confirmations.missing_alt_text.confirm": "Alt-tekst toevoegen",
"confirmations.missing_alt_text.message": "Je bericht bevat media zonder alt-tekst. Het toevoegen van beschrijvingen helpt je om je inhoud toegankelijk te maken voor meer mensen.",
"confirmations.missing_alt_text.secondary": "Toch plaatsen",
"confirmations.missing_alt_text.title": "Alt-tekst toevoegen?",
"confirmations.mute.confirm": "Negeren", "confirmations.mute.confirm": "Negeren",
"confirmations.redraft.confirm": "Verwijderen en herschrijven", "confirmations.redraft.confirm": "Verwijderen en herschrijven",
"confirmations.redraft.message": "Weet je zeker dat je dit bericht wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en de reacties op het originele bericht raak je kwijt.", "confirmations.redraft.message": "Weet je zeker dat je dit bericht wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en de reacties op het originele bericht raak je kwijt.",
@ -301,7 +305,7 @@
"empty_column.explore_statuses": "Momenteel zijn er geen trends. Kom later terug!", "empty_column.explore_statuses": "Momenteel zijn er geen trends. Kom later terug!",
"empty_column.favourited_statuses": "Jij hebt nog geen favoriete berichten. Wanneer je een bericht als favoriet markeert, valt deze hier te zien.", "empty_column.favourited_statuses": "Jij hebt nog geen favoriete berichten. Wanneer je een bericht als favoriet markeert, valt deze hier te zien.",
"empty_column.favourites": "Niemand heeft dit bericht nog als favoriet gemarkeerd. Wanneer iemand dit doet, valt dat hier te zien.", "empty_column.favourites": "Niemand heeft dit bericht nog als favoriet gemarkeerd. Wanneer iemand dit doet, valt dat hier te zien.",
"empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.", "empty_column.follow_requests": "Je hebt nog geen volgverzoeken ontvangen. Wanneer je er een ontvangt, valt dat hier te zien.",
"empty_column.followed_tags": "Je hebt nog geen hashtags gevolgd. Nadat je dit doet, komen deze hier te staan.", "empty_column.followed_tags": "Je hebt nog geen hashtags gevolgd. Nadat je dit doet, komen deze hier te staan.",
"empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.", "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
"empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen.", "empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen.",

View file

@ -211,6 +211,10 @@
"confirmations.logout.confirm": "Logg ut", "confirmations.logout.confirm": "Logg ut",
"confirmations.logout.message": "Er du sikker på at du vil logga ut?", "confirmations.logout.message": "Er du sikker på at du vil logga ut?",
"confirmations.logout.title": "Logg ut?", "confirmations.logout.title": "Logg ut?",
"confirmations.missing_alt_text.confirm": "Legg til alternativ tekst",
"confirmations.missing_alt_text.message": "Posten din inneheld media utan alternativ-tekst. Det å leggje til skildringar hjelper med gjere innhaldet ditt tilgjengeleg for fleire personar.",
"confirmations.missing_alt_text.secondary": "Publiser likevel",
"confirmations.missing_alt_text.title": "Legg til alternativ tekst?",
"confirmations.mute.confirm": "Demp", "confirmations.mute.confirm": "Demp",
"confirmations.redraft.confirm": "Slett & skriv på nytt", "confirmations.redraft.confirm": "Slett & skriv på nytt",
"confirmations.redraft.message": "Er du sikker på at du vil sletta denne statusen og skriva han på nytt? Då misser du favorittar og framhevingar, og svar til det opprinnelege innlegget vert foreldrelause.", "confirmations.redraft.message": "Er du sikker på at du vil sletta denne statusen og skriva han på nytt? Då misser du favorittar og framhevingar, og svar til det opprinnelege innlegget vert foreldrelause.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Terminar sessão", "confirmations.logout.confirm": "Terminar sessão",
"confirmations.logout.message": "Tens a certeza de que queres terminar a sessão?", "confirmations.logout.message": "Tens a certeza de que queres terminar a sessão?",
"confirmations.logout.title": "Terminar sessão?", "confirmations.logout.title": "Terminar sessão?",
"confirmations.missing_alt_text.confirm": "Adicionar texto alternativo",
"confirmations.missing_alt_text.message": "A sua publicação contém elementos gráficos sem texto alternativo. Adicionar descrições ajuda a tornar o seu conteúdo acessível a mais pessoas.",
"confirmations.missing_alt_text.secondary": "Publicar mesmo assim",
"confirmations.missing_alt_text.title": "Adicionar texto alternativo?",
"confirmations.mute.confirm": "Ocultar", "confirmations.mute.confirm": "Ocultar",
"confirmations.redraft.confirm": "Eliminar e reescrever", "confirmations.redraft.confirm": "Eliminar e reescrever",
"confirmations.redraft.message": "Tens a certeza de que queres eliminar e tornar a escrever esta publicação? Os favoritos e as publicações impulsionadas perder-se-ão e as respostas à publicação original ficarão órfãs.", "confirmations.redraft.message": "Tens a certeza de que queres eliminar e tornar a escrever esta publicação? Os favoritos e as publicações impulsionadas perder-se-ão e as respostas à publicação original ficarão órfãs.",

View file

@ -86,6 +86,9 @@
"alert.unexpected.message": "Vyskytla sa nečakaná chyba.", "alert.unexpected.message": "Vyskytla sa nečakaná chyba.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"alt_text_badge.title": "Alternatívny popis", "alt_text_badge.title": "Alternatívny popis",
"alt_text_modal.add_text_from_image": "Pridaj text z obrázka",
"alt_text_modal.cancel": "Zrušiť",
"alt_text_modal.done": "Hotovo",
"announcement.announcement": "Oznámenie", "announcement.announcement": "Oznámenie",
"annual_report.summary.archetype.oracle": "Veštec", "annual_report.summary.archetype.oracle": "Veštec",
"annual_report.summary.followers.followers": "sledovatelia", "annual_report.summary.followers.followers": "sledovatelia",
@ -378,6 +381,7 @@
"ignore_notifications_modal.not_followers_title": "Nevšímať si oznámenia od ľudí, ktorí ťa nenasledujú?", "ignore_notifications_modal.not_followers_title": "Nevšímať si oznámenia od ľudí, ktorí ťa nenasledujú?",
"ignore_notifications_modal.not_following_title": "Nevšímať si oznámenia od ľudí, ktorých nenasleduješ?", "ignore_notifications_modal.not_following_title": "Nevšímať si oznámenia od ľudí, ktorých nenasleduješ?",
"ignore_notifications_modal.private_mentions_title": "Nevšímať si oznámenia o nevyžiadaných súkromných spomínaniach?", "ignore_notifications_modal.private_mentions_title": "Nevšímať si oznámenia o nevyžiadaných súkromných spomínaniach?",
"info_button.label": "Pomoc",
"interaction_modal.action.favourite": "Pre pokračovanie si musíš obľúbiť zo svojho účtu.", "interaction_modal.action.favourite": "Pre pokračovanie si musíš obľúbiť zo svojho účtu.",
"interaction_modal.action.follow": "Pre pokračovanie musíš nasledovať zo svojho účtu.", "interaction_modal.action.follow": "Pre pokračovanie musíš nasledovať zo svojho účtu.",
"interaction_modal.action.reply": "Pre pokračovanie musíš odpovedať s tvojho účtu.", "interaction_modal.action.reply": "Pre pokračovanie musíš odpovedať s tvojho účtu.",

View file

@ -213,6 +213,10 @@
"confirmations.logout.confirm": "Dilni", "confirmations.logout.confirm": "Dilni",
"confirmations.logout.message": "Jeni i sigurt se doni të dilet?", "confirmations.logout.message": "Jeni i sigurt se doni të dilet?",
"confirmations.logout.title": "Të dilet?", "confirmations.logout.title": "Të dilet?",
"confirmations.missing_alt_text.confirm": "Shtoni tekst alternativ",
"confirmations.missing_alt_text.message": "Postimi juaj përmban media pa tekst alternativ. Shtimi i përshkrimeve ndihmon të bëhet lënda juaj e përdorshme nga më tepër njerëz.",
"confirmations.missing_alt_text.secondary": "Postoje, sido qoftë",
"confirmations.missing_alt_text.title": "Të shtohet tekst alternativ?",
"confirmations.mute.confirm": "Heshtoje", "confirmations.mute.confirm": "Heshtoje",
"confirmations.redraft.confirm": "Fshijeni & rihartojeni", "confirmations.redraft.confirm": "Fshijeni & rihartojeni",
"confirmations.redraft.message": "Jeni i sigurt se doni të fshihet kjo gjendje dhe të rihartohet? Të parapëlqyerit dhe përforcimet do të humbin, ndërsa përgjigjet te postimi origjinal do të bëhen jetime.", "confirmations.redraft.message": "Jeni i sigurt se doni të fshihet kjo gjendje dhe të rihartohet? Të parapëlqyerit dhe përforcimet do të humbin, ndërsa përgjigjet te postimi origjinal do të bëhen jetime.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "Oturumu kapat", "confirmations.logout.confirm": "Oturumu kapat",
"confirmations.logout.message": "Oturumu kapatmak istediğinden emin misin?", "confirmations.logout.message": "Oturumu kapatmak istediğinden emin misin?",
"confirmations.logout.title": "Oturumu kapat?", "confirmations.logout.title": "Oturumu kapat?",
"confirmations.missing_alt_text.confirm": "Alternatif metin ekle",
"confirmations.missing_alt_text.message": "Gönderiniz alternatif metni olmayan medya içeriyor. Tanımlamalar eklemek, içeriğinizi insanlar açısından daha erişilebilir kılar.",
"confirmations.missing_alt_text.secondary": "Yine de gönder",
"confirmations.missing_alt_text.title": "Alternatif metin ekle?",
"confirmations.mute.confirm": "Sessize al", "confirmations.mute.confirm": "Sessize al",
"confirmations.redraft.confirm": "Sil Düzenle ve yeniden paylaş", "confirmations.redraft.confirm": "Sil Düzenle ve yeniden paylaş",
"confirmations.redraft.message": "Bu gönderiyi silip taslak haline getirmek istediğinize emin misiniz? Mevcut favoriler ve boostlar silinecek ve gönderiye verilen yanıtlar başıboş kalacak.", "confirmations.redraft.message": "Bu gönderiyi silip taslak haline getirmek istediğinize emin misiniz? Mevcut favoriler ve boostlar silinecek ve gönderiye verilen yanıtlar başıboş kalacak.",

View file

@ -218,6 +218,9 @@
"confirmations.logout.confirm": "Вийти", "confirmations.logout.confirm": "Вийти",
"confirmations.logout.message": "Ви впевнені, що хочете вийти?", "confirmations.logout.message": "Ви впевнені, що хочете вийти?",
"confirmations.logout.title": "Вийти?", "confirmations.logout.title": "Вийти?",
"confirmations.missing_alt_text.confirm": "Додати альтернативний текст",
"confirmations.missing_alt_text.secondary": "Все одно опублікувати",
"confirmations.missing_alt_text.title": "Додати альтернативний текст?",
"confirmations.mute.confirm": "Приховати", "confirmations.mute.confirm": "Приховати",
"confirmations.redraft.confirm": "Видалити та виправити", "confirmations.redraft.confirm": "Видалити та виправити",
"confirmations.redraft.message": "Ви впевнені, що хочете видалити цей допис та переписати його? Додавання у вибране та поширення буде втрачено, а відповіді на оригінальний допис залишаться без першоджерела.", "confirmations.redraft.message": "Ви впевнені, що хочете видалити цей допис та переписати його? Додавання у вибране та поширення буде втрачено, а відповіді на оригінальний допис залишаться без першоджерела.",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "退出登录", "confirmations.logout.confirm": "退出登录",
"confirmations.logout.message": "确定要退出登录吗?", "confirmations.logout.message": "确定要退出登录吗?",
"confirmations.logout.title": "确定要退出登录?", "confirmations.logout.title": "确定要退出登录?",
"confirmations.missing_alt_text.confirm": "添加替代文本",
"confirmations.missing_alt_text.message": "您的帖子包含没有添加替代文本的媒体。添加描述有助于使更多人访问您的内容。",
"confirmations.missing_alt_text.secondary": "就这样发布",
"confirmations.missing_alt_text.title": "添加替代文本?",
"confirmations.mute.confirm": "隐藏", "confirmations.mute.confirm": "隐藏",
"confirmations.redraft.confirm": "删除并重新编辑", "confirmations.redraft.confirm": "删除并重新编辑",
"confirmations.redraft.message": "确定删除这条嘟文并重写吗?所有相关的喜欢和转嘟都将丢失,嘟文的回复也会失去关联。", "confirmations.redraft.message": "确定删除这条嘟文并重写吗?所有相关的喜欢和转嘟都将丢失,嘟文的回复也会失去关联。",

View file

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "登出", "confirmations.logout.confirm": "登出",
"confirmations.logout.message": "您確定要登出嗎?", "confirmations.logout.message": "您確定要登出嗎?",
"confirmations.logout.title": "您確定要登出嗎?", "confirmations.logout.title": "您確定要登出嗎?",
"confirmations.missing_alt_text.confirm": "新增說明文字",
"confirmations.missing_alt_text.message": "您的嘟文中的多媒體內容未附上說明文字。添加說明文字描述能幫助更多人存取您的內容。",
"confirmations.missing_alt_text.secondary": "仍要發嘟",
"confirmations.missing_alt_text.title": "是否新增說明文字?",
"confirmations.mute.confirm": "靜音", "confirmations.mute.confirm": "靜音",
"confirmations.redraft.confirm": "刪除並重新編輯", "confirmations.redraft.confirm": "刪除並重新編輯",
"confirmations.redraft.message": "您確定要刪除這則嘟文並重新編輯嗎?您將失去這則嘟文之轉嘟及最愛,且對此嘟文之回覆會變成獨立的嘟文。", "confirmations.redraft.message": "您確定要刪除這則嘟文並重新編輯嗎?您將失去這則嘟文之轉嘟及最愛,且對此嘟文之回覆會變成獨立的嘟文。",

View file

@ -4202,23 +4202,27 @@ a.status-card {
} }
.load-more { .load-more {
display: block; display: flex;
align-items: center;
justify-content: center;
color: $dark-text-color; color: $dark-text-color;
background-color: transparent; background-color: transparent;
border: 0; border: 0;
font-size: inherit; font-size: inherit;
text-align: center;
line-height: inherit; line-height: inherit;
margin: 0; width: 100%;
padding: 15px; padding: 15px;
box-sizing: border-box; box-sizing: border-box;
width: 100%;
clear: both;
text-decoration: none; text-decoration: none;
&:hover { &:hover {
background: var(--on-surface-color); background: var(--on-surface-color);
} }
.icon {
width: 22px;
height: 22px;
}
} }
.load-gap { .load-gap {
@ -4599,6 +4603,7 @@ a.status-card {
justify-content: center; justify-content: center;
} }
.load-more .loading-indicator,
.button .loading-indicator { .button .loading-indicator {
position: static; position: static;
transform: none; transform: none;
@ -4610,6 +4615,10 @@ a.status-card {
} }
} }
.load-more .loading-indicator .circular-progress {
color: lighten($ui-base-color, 26%);
}
.circular-progress { .circular-progress {
color: lighten($ui-base-color, 26%); color: lighten($ui-base-color, 26%);
animation: 1.4s linear 0s infinite normal none running simple-rotate; animation: 1.4s linear 0s infinite normal none running simple-rotate;

View file

@ -338,6 +338,38 @@ class FeedManager
end end
end end
# Populate list feed of account from scratch
# @param [List] list
# @return [void]
def populate_list(list)
limit = FeedManager::MAX_ITEMS / 2
aggregate = list.account.user&.aggregates_reblogs?
timeline_key = key(:list, list.id)
list.active_accounts.includes(:account_stat).reorder(nil).find_each do |target_account|
if redis.zcard(timeline_key) >= limit
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at)
# If the feed is full and this account has not posted more recently
# than the last item on the feed, then we can skip the whole account
# because none of its statuses would stay on the feed anyway
next if last_status_score < oldest_home_score
end
statuses = target_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, :account, reblog: :account).limit(limit)
crutches = build_crutches(list.account_id, statuses)
statuses.each do |status|
next if filter_from_home(status, list.account_id, crutches) || filter_from_list?(status, list)
add_to_feed(:list, list.id, status, aggregate_reblogs: aggregate)
end
trim(:list, list.id)
end
end
# Completely clear multiple feeds at once # Completely clear multiple feeds at once
# @param [Symbol] type # @param [Symbol] type
# @param [Array<Integer>] ids # @param [Array<Integer>] ids

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
# This implements an older draft of HTTP Signatures:
# https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures
class HttpSignatureDraft
REQUEST_TARGET = '(request-target)'
def initialize(keypair, key_id, full_path: true)
@keypair = keypair
@key_id = key_id
@full_path = full_path
end
def request_target(verb, url)
if url.query.nil? || !@full_path
"#{verb} #{url.path}"
else
"#{verb} #{url.path}?#{url.query}"
end
end
def sign(signed_headers, verb, url)
signed_headers = signed_headers.merge(REQUEST_TARGET => request_target(verb, url))
signed_string = signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n")
algorithm = 'rsa-sha256'
signature = Base64.strict_encode64(@keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
"keyId=\"#{@key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\""
end
end

View file

@ -61,8 +61,6 @@ class PerOperationWithDeadline < HTTP::Timeout::PerOperation
end end
class Request class Request
REQUEST_TARGET = '(request-target)'
# We enforce a 5s timeout on DNS resolving, 5s timeout on socket opening # We enforce a 5s timeout on DNS resolving, 5s timeout on socket opening
# and 5s timeout on the TLS handshake, meaning the worst case should take # and 5s timeout on the TLS handshake, meaning the worst case should take
# about 15s in total # about 15s in total
@ -78,11 +76,21 @@ class Request
@http_client = options.delete(:http_client) @http_client = options.delete(:http_client)
@allow_local = options.delete(:allow_local) @allow_local = options.delete(:allow_local)
@full_path = !options.delete(:omit_query_string) @full_path = !options.delete(:omit_query_string)
@options = options.merge(socket_class: use_proxy? || @allow_local ? ProxySocket : Socket) @options = {
@options = @options.merge(timeout_class: PerOperationWithDeadline, timeout_options: TIMEOUT) follow: {
max_hops: 3,
on_redirect: ->(response, request) { re_sign_on_redirect(response, request) },
},
}.merge(options).merge(
socket_class: use_proxy? || @allow_local ? ProxySocket : Socket,
timeout_class: PerOperationWithDeadline,
timeout_options: TIMEOUT
)
@options = @options.merge(proxy_url) if use_proxy? @options = @options.merge(proxy_url) if use_proxy?
@headers = {} @headers = {}
@signing = nil
raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if block_hidden_service? raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if block_hidden_service?
set_common_headers! set_common_headers!
@ -92,8 +100,9 @@ class Request
def on_behalf_of(actor, sign_with: nil) def on_behalf_of(actor, sign_with: nil)
raise ArgumentError, 'actor must not be nil' if actor.nil? raise ArgumentError, 'actor must not be nil' if actor.nil?
@actor = actor key_id = ActivityPub::TagManager.instance.key_uri_for(actor)
@keypair = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : @actor.keypair keypair = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : actor.keypair
@signing = HttpSignatureDraft.new(keypair, key_id, full_path: @full_path)
self self
end end
@ -119,7 +128,7 @@ class Request
end end
def headers def headers
(@actor ? @headers.merge('Signature' => signature) : @headers).without(REQUEST_TARGET) (@signing ? @headers.merge('Signature' => signature) : @headers)
end end
class << self class << self
@ -134,14 +143,13 @@ class Request
end end
def http_client def http_client
HTTP.use(:auto_inflate).follow(max_hops: 3) HTTP.use(:auto_inflate)
end end
end end
private private
def set_common_headers! def set_common_headers!
@headers[REQUEST_TARGET] = request_target
@headers['User-Agent'] = Mastodon::Version.user_agent @headers['User-Agent'] = Mastodon::Version.user_agent
@headers['Host'] = @url.host @headers['Host'] = @url.host
@headers['Date'] = Time.now.utc.httpdate @headers['Date'] = Time.now.utc.httpdate
@ -152,31 +160,28 @@ class Request
@headers['Digest'] = "SHA-256=#{Digest::SHA256.base64digest(@options[:body])}" @headers['Digest'] = "SHA-256=#{Digest::SHA256.base64digest(@options[:body])}"
end end
def request_target
if @url.query.nil? || !@full_path
"#{@verb} #{@url.path}"
else
"#{@verb} #{@url.path}?#{@url.query}"
end
end
def signature def signature
algorithm = 'rsa-sha256' @signing.sign(@headers.without('User-Agent', 'Accept-Encoding'), @verb, @url)
signature = Base64.strict_encode64(@keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
"keyId=\"#{key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\""
end end
def signed_string def re_sign_on_redirect(_response, request)
signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n") # Delete existing signature if there is one, since it will be invalid
request.headers.delete('Signature')
return unless @signing.present? && @verb == :get
signed_headers = request.headers.to_h.slice(*@headers.keys)
unless @headers.keys.all? { |key| signed_headers.key?(key) }
# We have lost some headers in the process, so don't sign the new
# request, in order to avoid issuing a valid signature with fewer
# conditions than expected.
Rails.logger.warn { "Some headers (#{@headers.keys - signed_headers.keys}) have been lost on redirect from {@uri} to #{request.uri}, this should not happen. Skipping signatures" }
return
end end
def signed_headers signature_value = @signing.sign(signed_headers.without('User-Agent', 'Accept-Encoding'), @verb, Addressable::URI.parse(request.uri))
@headers.without('User-Agent', 'Accept-Encoding') request.headers['Signature'] = signature_value
end
def key_id
ActivityPub::TagManager.instance.key_uri_for(@actor)
end end
def http_client def http_client

View file

@ -26,6 +26,7 @@ class List < ApplicationRecord
has_many :list_accounts, inverse_of: :list, dependent: :destroy has_many :list_accounts, inverse_of: :list, dependent: :destroy
has_many :accounts, through: :list_accounts has_many :accounts, through: :list_accounts
has_many :active_accounts, -> { merge(ListAccount.active) }, through: :list_accounts, source: :account
has_many :antennas, inverse_of: :list, dependent: :destroy has_many :antennas, inverse_of: :list, dependent: :destroy
has_many :list_statuses, inverse_of: :list, dependent: :destroy has_many :list_statuses, inverse_of: :list, dependent: :destroy
has_many :statuses, through: :list_statuses has_many :statuses, through: :list_statuses

View file

@ -20,6 +20,8 @@ class ListAccount < ApplicationRecord
validates :account_id, uniqueness: { scope: :list_id } validates :account_id, uniqueness: { scope: :list_id }
validate :validate_relationship validate :validate_relationship
scope :active, -> { where.not(follow_id: nil) }
before_validation :set_follow, unless: :list_owner_account_is_account? before_validation :set_follow, unless: :list_owner_account_is_account?
private private

View file

@ -37,7 +37,8 @@ class Poll < ApplicationRecord
validates :options, presence: true validates :options, presence: true
validates :expires_at, presence: true, if: :local? validates :expires_at, presence: true, if: :local?
validates_with PollValidator, on: :create, if: :local? validates_with PollOptionsValidator, if: :local?
validates_with PollExpirationValidator, if: -> { local? && expires_at_changed? }
before_validation :prepare_options, if: :local? before_validation :prepare_options, if: :local?
before_validation :prepare_votes_count before_validation :prepare_votes_count

View file

@ -68,6 +68,7 @@ class UserSettings
setting :enable_emoji_reaction, default: true setting :enable_emoji_reaction, default: true
setting :show_emoji_reaction_on_timeline, default: true setting :show_emoji_reaction_on_timeline, default: true
setting :reblog_modal, default: false setting :reblog_modal, default: false
setting :missing_alt_text_modal, default: true
setting :reduce_motion, default: false setting :reduce_motion, default: false
setting :expand_content_warnings, default: false setting :expand_content_warnings, default: false
setting :display_media, default: 'default', in: %w(default show_all hide_all) setting :display_media, default: 'default', in: %w(default show_all hide_all)

View file

@ -21,6 +21,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:me] = object.current_account.id.to_s store[:me] = object.current_account.id.to_s
store[:boost_modal] = object_account_user.setting_boost_modal store[:boost_modal] = object_account_user.setting_boost_modal
store[:delete_modal] = object_account_user.setting_delete_modal store[:delete_modal] = object_account_user.setting_delete_modal
store[:missing_alt_text_modal] = object_account_user.settings['web.missing_alt_text_modal']
store[:auto_play_gif] = object_account_user.setting_auto_play_gif store[:auto_play_gif] = object_account_user.setting_auto_play_gif
store[:display_media] = object_account_user.setting_display_media store[:display_media] = object_account_user.setting_display_media
store[:expand_spoilers] = object_account_user.setting_expand_spoilers store[:expand_spoilers] = object_account_user.setting_expand_spoilers

View file

@ -91,10 +91,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
}, },
polls: { polls: {
max_options: PollValidator::MAX_OPTIONS, max_options: PollOptionsValidator::MAX_OPTIONS,
max_characters_per_option: PollValidator::MAX_OPTION_CHARS, max_characters_per_option: PollOptionsValidator::MAX_OPTION_CHARS,
min_expiration: PollValidator::MIN_EXPIRATION, min_expiration: PollExpirationValidator::MIN_EXPIRATION,
max_expiration: PollValidator::MAX_EXPIRATION, max_expiration: PollExpirationValidator::MAX_EXPIRATION,
allow_image: true, allow_image: true,
}, },

View file

@ -74,10 +74,10 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer
}, },
polls: { polls: {
max_options: PollValidator::MAX_OPTIONS, max_options: PollOptionsValidator::MAX_OPTIONS,
max_characters_per_option: PollValidator::MAX_OPTION_CHARS, max_characters_per_option: PollOptionsValidator::MAX_OPTION_CHARS,
min_expiration: PollValidator::MIN_EXPIRATION, min_expiration: PollExpirationValidator::MIN_EXPIRATION,
max_expiration: PollValidator::MAX_EXPIRATION, max_expiration: PollExpirationValidator::MAX_EXPIRATION,
allow_image: true, allow_image: true,
}, },

View file

@ -5,6 +5,10 @@ class PrecomputeFeedService < BaseService
def call(account) def call(account)
FeedManager.instance.populate_home(account) FeedManager.instance.populate_home(account)
account.owned_lists.each do |list|
FeedManager.instance.populate_list(list)
end
ensure ensure
redis.del("account:#{account.id}:regeneration") redis.del("account:#{account.id}:regeneration")
end end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
class PollExpirationValidator < ActiveModel::Validator
MAX_EXPIRATION = 1.month.freeze
MIN_EXPIRATION = 5.minutes.freeze
def validate(poll)
# We have a `presence: true` check for this attribute already
return if poll.expires_at.nil?
current_time = Time.now.utc
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_long')) if poll.expires_at - current_time > MAX_EXPIRATION
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_short')) if (poll.expires_at - current_time).ceil < MIN_EXPIRATION
end
end

View file

@ -1,19 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
class PollValidator < ActiveModel::Validator class PollOptionsValidator < ActiveModel::Validator
MAX_OPTIONS = 8 MAX_OPTIONS = 8
MAX_OPTION_CHARS = 50 MAX_OPTION_CHARS = 50
MAX_EXPIRATION = 1.month.freeze
MIN_EXPIRATION = 5.minutes.freeze
def validate(poll) def validate(poll)
current_time = Time.now.utc
poll.errors.add(:options, I18n.t('polls.errors.too_few_options')) unless poll.options.size > 1 poll.errors.add(:options, I18n.t('polls.errors.too_few_options')) unless poll.options.size > 1
poll.errors.add(:options, I18n.t('polls.errors.too_many_options', max: MAX_OPTIONS)) if poll.options.size > MAX_OPTIONS poll.errors.add(:options, I18n.t('polls.errors.too_many_options', max: MAX_OPTIONS)) if poll.options.size > MAX_OPTIONS
poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.mb_chars.grapheme_length > MAX_OPTION_CHARS } poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.mb_chars.grapheme_length > MAX_OPTION_CHARS }
poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq.size == poll.options.size poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq.size == poll.options.size
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_long')) if poll.expires_at.nil? || poll.expires_at - current_time > MAX_EXPIRATION
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_short')) if poll.expires_at.present? && (poll.expires_at - current_time).ceil < MIN_EXPIRATION
end end
end end

View file

@ -3,7 +3,7 @@
.input-copy .input-copy
.input-copy__wrapper .input-copy__wrapper
= copyable_input value: public_invite_url(invite_code: invite.code) = copyable_input value: public_invite_url(invite_code: invite.code)
%button{ type: :button }= t('generic.copy') %button.button{ type: :button }= t('generic.copy')
%td %td
.name-tag .name-tag

View file

@ -3,7 +3,7 @@
.input-copy .input-copy
.input-copy__wrapper .input-copy__wrapper
= copyable_input value: public_invite_url(invite_code: invite.code) = copyable_input value: public_invite_url(invite_code: invite.code)
%button{ type: :button }= t('generic.copy') %button.button{ type: :button }= t('generic.copy')
- if invite.valid_for_use? - if invite.valid_for_use?
%td %td

View file

@ -16,4 +16,5 @@
= form.hidden_field :type, = form.hidden_field :type,
value: params[:type] value: params[:type]
= form.button t('mail_subscriptions.unsubscribe.action'), = form.button t('mail_subscriptions.unsubscribe.action'),
type: :submit type: :submit,
class: 'btn'

View file

@ -35,7 +35,8 @@
= form.hidden_field :scope, = form.hidden_field :scope,
value: @pre_auth.scope value: @pre_auth.scope
= form.button t('doorkeeper.authorizations.buttons.authorize'), = form.button t('doorkeeper.authorizations.buttons.authorize'),
type: :submit type: :submit,
class: 'btn'
= form_with url: oauth_authorization_path, method: :delete do |form| = form_with url: oauth_authorization_path, method: :delete do |form|
= form.hidden_field :client_id, = form.hidden_field :client_id,
@ -52,4 +53,4 @@
value: @pre_auth.scope value: @pre_auth.scope
= form.button t('doorkeeper.authorizations.buttons.deny'), = form.button t('doorkeeper.authorizations.buttons.deny'),
type: :submit, type: :submit,
class: 'negative' class: 'btn negative'

View file

@ -4,4 +4,4 @@
.input-copy .input-copy
.input-copy__wrapper .input-copy__wrapper
= copyable_input value: params[:code], class: 'oauth-code' = copyable_input value: params[:code], class: 'oauth-code'
%button{ type: :button }= t('generic.copy') %button.button{ type: :button }= t('generic.copy')

View file

@ -119,6 +119,7 @@
.fields-group .fields-group
= ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal') = ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal')
= ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal') = ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal')
= ff.input :'web.missing_alt_text_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_missing_alt_text_modal')
%h4= t 'appearance.sensitive_content' %h4= t 'appearance.sensitive_content'

View file

@ -17,7 +17,7 @@
.input-copy.lead .input-copy.lead
.input-copy__wrapper .input-copy__wrapper
= copyable_input value: link_to('Mastodon', ActivityPub::TagManager.instance.url_for(@account), rel: :me) = copyable_input value: link_to('Mastodon', ActivityPub::TagManager.instance.url_for(@account), rel: :me)
%button{ type: :button }= t('generic.copy') %button.button{ type: :button }= t('generic.copy')
%p.lead= t('verification.extra_instructions_html') %p.lead= t('verification.extra_instructions_html')
@ -60,7 +60,7 @@
.input-copy.lead .input-copy.lead
.input-copy__wrapper .input-copy__wrapper
= copyable_input value: tag.meta(name: 'fediverse:creator', content: "@#{@account.local_username_and_domain}") = copyable_input value: tag.meta(name: 'fediverse:creator', content: "@#{@account.local_username_and_domain}")
%button{ type: :button }= t('generic.copy') %button.button{ type: :button }= t('generic.copy')
%p.lead= t('author_attribution.then_instructions') %p.lead= t('author_attribution.then_instructions')

View file

@ -23,6 +23,8 @@ fy:
models: models:
account: account:
attributes: attributes:
fields:
fields_with_values_missing_labels: befettet wearden mei ûntbrekkende labels
username: username:
invalid: mei allinnich letters, nûmers en ûnderstreekjes befetsje invalid: mei allinnich letters, nûmers en ûnderstreekjes befetsje
reserved: reservearre reserved: reservearre

View file

@ -1247,6 +1247,7 @@ cs:
too_fast: Formulář byl odeslán příliš rychle, zkuste to znovu. too_fast: Formulář byl odeslán příliš rychle, zkuste to znovu.
use_security_key: Použít bezpečnostní klíč use_security_key: Použít bezpečnostní klíč
user_agreement_html: Přečetl jsem si a souhlasím s <a href="%{terms_of_service_path}" target="_blank">podmínkami služby</a> a <a href="%{privacy_policy_path}" target="_blank">ochranou osobních údajů</a> user_agreement_html: Přečetl jsem si a souhlasím s <a href="%{terms_of_service_path}" target="_blank">podmínkami služby</a> a <a href="%{privacy_policy_path}" target="_blank">ochranou osobních údajů</a>
user_privacy_agreement_html: Četl jsem a souhlasím se zásadami <a href="%{privacy_policy_path}" target="_blank">ochrany osobních údajů</a>
author_attribution: author_attribution:
example_title: Ukázkový text example_title: Ukázkový text
hint_html: Píšete novinové články nebo blog mimo Mastodon? Kontrolujte, jak Vám bude připisováno autorství, když jsou sdíleny na Mastodonu. hint_html: Píšete novinové články nebo blog mimo Mastodon? Kontrolujte, jak Vám bude připisováno autorství, když jsou sdíleny na Mastodonu.

View file

@ -948,6 +948,9 @@ fo:
preview: preview:
explanation_html: 'Teldubrævið verður sent til <strong>%{display_count} brúkarar</strong>, sum hava stovna kontu áðrenn %{date}. Fylgjandi tekstur kemur við í teldubrævið:' explanation_html: 'Teldubrævið verður sent til <strong>%{display_count} brúkarar</strong>, sum hava stovna kontu áðrenn %{date}. Fylgjandi tekstur kemur við í teldubrævið:'
send_preview: Send undanvísing til %{email} send_preview: Send undanvísing til %{email}
send_to_all:
one: Send %{display_count} teldubræv
other: Send %{display_count} teldubrøv
title: Undanvís fráboðan um tænastutreytir title: Undanvís fráboðan um tænastutreytir
publish: Útgev publish: Útgev
published_on_html: Útgivið %{date} published_on_html: Útgivið %{date}

View file

@ -1209,6 +1209,7 @@ fy:
too_fast: Formulier is te fluch yntsjinne. Probearje it nochris. too_fast: Formulier is te fluch yntsjinne. Probearje it nochris.
use_security_key: Befeiligingskaai brûke use_security_key: Befeiligingskaai brûke
user_agreement_html: Ik haw de <a href="%{terms_of_service_path}" target="_blank">gebrûksbetingsten</a> en it <a href="%{privacy_policy_path}" target="_blank">privacybelied</a> lêzen en gean der mei akkoard user_agreement_html: Ik haw de <a href="%{terms_of_service_path}" target="_blank">gebrûksbetingsten</a> en it <a href="%{privacy_policy_path}" target="_blank">privacybelied</a> lêzen en gean der mei akkoard
user_privacy_agreement_html: Ik haw it <a href="%{privacy_policy_path}" target="_blank">privacybelied</a> lêzen en gean dêrmei akkoard
author_attribution: author_attribution:
example_title: Faorbyldtekst example_title: Faorbyldtekst
hint_html: Skriuwe jo nijs- of blogartikelen bûten Mastodon? Bepaal hoet jo oahelle wurde as dizze dield wurde op Mastodon. hint_html: Skriuwe jo nijs- of blogartikelen bûten Mastodon? Bepaal hoet jo oahelle wurde as dizze dield wurde op Mastodon.

View file

@ -176,7 +176,7 @@ gl:
approve_appeal: Aprobar apelación approve_appeal: Aprobar apelación
approve_user: Aprobar Usuaria approve_user: Aprobar Usuaria
assigned_to_self_report: Asignar denuncia assigned_to_self_report: Asignar denuncia
change_email_user: Cambiar o correo da Usuaria change_email_user: Cambiar o correo da usuaria
change_role_user: Cambiar Rol da Usuaria change_role_user: Cambiar Rol da Usuaria
confirm_user: Confirmar usuaria confirm_user: Confirmar usuaria
create_account_warning: Crear aviso create_account_warning: Crear aviso

View file

@ -302,7 +302,7 @@ ko:
deleted_account: 계정을 삭제했습니다 deleted_account: 계정을 삭제했습니다
empty: 로그를 찾을 수 없습니다 empty: 로그를 찾을 수 없습니다
filter_by_action: 동작 별 필터 filter_by_action: 동작 별 필터
filter_by_user: 사용자로 거르기 filter_by_user: 사용자 기준으로 필터
title: 감사 로그 title: 감사 로그
unavailable_instance: "(도메인네임 사용불가)" unavailable_instance: "(도메인네임 사용불가)"
announcements: announcements:
@ -1192,6 +1192,7 @@ ko:
too_fast: 너무 빠르게 양식이 제출되었습니다, 다시 시도하세요. too_fast: 너무 빠르게 양식이 제출되었습니다, 다시 시도하세요.
use_security_key: 보안 키 사용 use_security_key: 보안 키 사용
user_agreement_html: <a href="%{terms_of_service_path}" target="_blank">이용 약관</a>과 <a href="%{privacy_policy_path}" target="_blank">개인정보처리방침</a>을 읽었고 동의합니다 user_agreement_html: <a href="%{terms_of_service_path}" target="_blank">이용 약관</a>과 <a href="%{privacy_policy_path}" target="_blank">개인정보처리방침</a>을 읽었고 동의합니다
user_privacy_agreement_html: <a href="%{privacy_policy_path}" target="_blank">개인정보처리방침</a>을 읽었고 동의합니다
author_attribution: author_attribution:
example_title: 예시 텍스트 example_title: 예시 텍스트
hint_html: 마스토돈 밖에서 뉴스나 블로그 글을 쓰시나요? 마스토돈에 공유되었을 때 어떻게 표시될지를 제어하세요. hint_html: 마스토돈 밖에서 뉴스나 블로그 글을 쓰시나요? 마스토돈에 공유되었을 때 어떻게 표시될지를 제어하세요.

View file

@ -1209,6 +1209,7 @@ nn:
too_fast: Skjemaet ble sendt inn for raskt, prøv på nytt. too_fast: Skjemaet ble sendt inn for raskt, prøv på nytt.
use_security_key: Bruk sikkerhetsnøkkel use_security_key: Bruk sikkerhetsnøkkel
user_agreement_html: Eg godtek <a href="%{terms_of_service_path}" target="_blank">bruksvilkåra</a> og <a href="%{privacy_policy_path}" target="_blank">personvernvllkåra</a> user_agreement_html: Eg godtek <a href="%{terms_of_service_path}" target="_blank">bruksvilkåra</a> og <a href="%{privacy_policy_path}" target="_blank">personvernvllkåra</a>
user_privacy_agreement_html: Eg har lese og godtar <a href="%{privacy_policy_path}" target="_blank">personvernerklæringa</a>
author_attribution: author_attribution:
example_title: Eksempeltekst example_title: Eksempeltekst
hint_html: Skriv du nyhende eller blogginnlegg utanfor Mastodon? Her kan du kontrollera korleis du blir kreditert når artiklane dine blir delte på Mastodon. hint_html: Skriv du nyhende eller blogginnlegg utanfor Mastodon? Her kan du kontrollera korleis du blir kreditert når artiklane dine blir delte på Mastodon.

View file

@ -134,6 +134,7 @@ bg:
changelog: Може да се структурира със синтаксиса на Markdown. changelog: Може да се структурира със синтаксиса на Markdown.
text: Може да се структурира със синтаксиса на Markdown. text: Може да се структурира със синтаксиса на Markdown.
terms_of_service_generator: terms_of_service_generator:
admin_email: Правните бележки включват насрещни известия, постановления на съда, заявки за сваляне и заявки от правоохранителните органи.
domain: Неповторимо идентифициране на онлайн услугата, която предоставяте. domain: Неповторимо идентифициране на онлайн услугата, която предоставяте.
jurisdiction: Впишете държавата, където живее всеки, който плаща сметките. Ако е дружество или друго образувание, то впишете държавата, в която е регистрирано, и градът, регионът, територията или щатът според случая. jurisdiction: Впишете държавата, където живее всеки, който плаща сметките. Ако е дружество или друго образувание, то впишете държавата, в която е регистрирано, и градът, регионът, територията или щатът според случая.
user: user:

View file

@ -225,6 +225,7 @@ ca:
setting_display_media_show_all: Mostra-ho tot setting_display_media_show_all: Mostra-ho tot
setting_expand_spoilers: Desplega sempre els tuts marcats amb advertències de contingut setting_expand_spoilers: Desplega sempre els tuts marcats amb advertències de contingut
setting_hide_network: Amaga la teva xarxa setting_hide_network: Amaga la teva xarxa
setting_missing_alt_text_modal: Mostra un diàleg de confirmació abans de publicar contingut sense text alternatiu
setting_reduce_motion: Redueix el moviment de les animacions setting_reduce_motion: Redueix el moviment de les animacions
setting_system_font_ui: Usa la lletra predeterminada del sistema setting_system_font_ui: Usa la lletra predeterminada del sistema
setting_system_scrollbars_ui: Usa la barra de desplaçament predeterminada del sistema setting_system_scrollbars_ui: Usa la barra de desplaçament predeterminada del sistema

View file

@ -3,6 +3,7 @@ cs:
simple_form: simple_form:
hints: hints:
account: account:
attribution_domains: Jeden na řádek. Chrání před falešným připisování autorství.
discoverable: Vaše veřejné příspěvky a profil mohou být zobrazeny nebo doporučeny v různých oblastech Mastodonu a váš profil může být navrhován ostatním uživatelům. discoverable: Vaše veřejné příspěvky a profil mohou být zobrazeny nebo doporučeny v různých oblastech Mastodonu a váš profil může být navrhován ostatním uživatelům.
display_name: Vaše celé jméno nebo přezdívka. display_name: Vaše celé jméno nebo přezdívka.
fields: Vaše domovská stránka, zájmena, věk, cokoliv chcete. fields: Vaše domovská stránka, zájmena, věk, cokoliv chcete.
@ -155,6 +156,7 @@ cs:
url: Kam budou události odesílány url: Kam budou události odesílány
labels: labels:
account: account:
attribution_domains: Webové stránky s povolením Vám připsat autorství
discoverable: Zobrazovat profil a příspěvky ve vyhledávacích algoritmech discoverable: Zobrazovat profil a příspěvky ve vyhledávacích algoritmech
fields: fields:
name: Označení name: Označení

View file

@ -233,6 +233,7 @@ da:
setting_display_media_show_all: Vis alle setting_display_media_show_all: Vis alle
setting_expand_spoilers: Ekspandér altid indlæg markeret med indholdsadvarsler setting_expand_spoilers: Ekspandér altid indlæg markeret med indholdsadvarsler
setting_hide_network: Skjul din sociale graf setting_hide_network: Skjul din sociale graf
setting_missing_alt_text_modal: Vis bekræftelsesdialog inden medier uden alt-tekst lægges op
setting_reduce_motion: Reducér animationsbevægelse setting_reduce_motion: Reducér animationsbevægelse
setting_system_font_ui: Brug systemets standardskrifttype setting_system_font_ui: Brug systemets standardskrifttype
setting_system_scrollbars_ui: Brug standard systemrullebjælke setting_system_scrollbars_ui: Brug standard systemrullebjælke

View file

@ -233,6 +233,7 @@ de:
setting_display_media_show_all: Alle Medien anzeigen setting_display_media_show_all: Alle Medien anzeigen
setting_expand_spoilers: Beiträge mit Inhaltswarnung immer ausklappen setting_expand_spoilers: Beiträge mit Inhaltswarnung immer ausklappen
setting_hide_network: Follower und „Folge ich“ nicht anzeigen setting_hide_network: Follower und „Folge ich“ nicht anzeigen
setting_missing_alt_text_modal: Bestätigungsdialog anzeigen, bevor Medien ohne Bildbeschreibung veröffentlicht werden
setting_reduce_motion: Bewegung in Animationen verringern setting_reduce_motion: Bewegung in Animationen verringern
setting_system_font_ui: Standardschriftart des Browsers verwenden setting_system_font_ui: Standardschriftart des Browsers verwenden
setting_system_scrollbars_ui: Bildlaufleiste des Systems verwenden setting_system_scrollbars_ui: Bildlaufleiste des Systems verwenden

View file

@ -312,6 +312,7 @@ en:
setting_hide_quote_unavailable_server: Hide quote menu from unavailable server setting_hide_quote_unavailable_server: Hide quote menu from unavailable server
setting_hide_status_reference_unavailable_server: Hide quiet quote (reference named by Fedibird) menu from unavailable server setting_hide_status_reference_unavailable_server: Hide quiet quote (reference named by Fedibird) menu from unavailable server
setting_lock_follow_from_bot: Request approval about bot follow setting_lock_follow_from_bot: Request approval about bot follow
setting_missing_alt_text_modal: Show confirmation dialog before posting media without alt text
setting_public_post_to_unlisted: Convert public post to public unlisted if not using Web app setting_public_post_to_unlisted: Convert public post to public unlisted if not using Web app
setting_reduce_motion: Reduce motion in animations setting_reduce_motion: Reduce motion in animations
setting_reject_public_unlisted_subscription: Reject sending public unlisted visibility/non-public searchability posts to Misskey, Calckey setting_reject_public_unlisted_subscription: Reject sending public unlisted visibility/non-public searchability posts to Misskey, Calckey

View file

@ -233,6 +233,7 @@ eo:
setting_display_media_show_all: Montri ĉiujn setting_display_media_show_all: Montri ĉiujn
setting_expand_spoilers: Ĉiam malfoldas mesaĝojn markitajn per averto pri enhavo setting_expand_spoilers: Ĉiam malfoldas mesaĝojn markitajn per averto pri enhavo
setting_hide_network: Kaŝi viajn sekvantojn kaj sekvatojn setting_hide_network: Kaŝi viajn sekvantojn kaj sekvatojn
setting_missing_alt_text_modal: Montru konfirman dialogon antaŭ afiŝado de amaskomunikiloj sen altteksto
setting_reduce_motion: Redukti la movecojn de la animacioj setting_reduce_motion: Redukti la movecojn de la animacioj
setting_system_font_ui: Uzi la dekomencan tiparon de la sistemo setting_system_font_ui: Uzi la dekomencan tiparon de la sistemo
setting_system_scrollbars_ui: Uzu la defaŭltan rulumilon de la sistemo setting_system_scrollbars_ui: Uzu la defaŭltan rulumilon de la sistemo

View file

@ -233,6 +233,7 @@ es-AR:
setting_display_media_show_all: Mostrar todo setting_display_media_show_all: Mostrar todo
setting_expand_spoilers: Siempre expandir los mensajes marcados con advertencias de contenido setting_expand_spoilers: Siempre expandir los mensajes marcados con advertencias de contenido
setting_hide_network: Ocultá tu gráfica social setting_hide_network: Ocultá tu gráfica social
setting_missing_alt_text_modal: Mostrar diálogo de confirmación antes de publicar medios sin texto alternativo
setting_reduce_motion: Reducir el movimiento de las animaciones setting_reduce_motion: Reducir el movimiento de las animaciones
setting_system_font_ui: Utilizar la tipografía predeterminada del sistema setting_system_font_ui: Utilizar la tipografía predeterminada del sistema
setting_system_scrollbars_ui: Usar la barra de desplazamiento predeterminada del sistema operativo setting_system_scrollbars_ui: Usar la barra de desplazamiento predeterminada del sistema operativo

View file

@ -233,6 +233,7 @@ es-MX:
setting_display_media_show_all: Mostrar todo setting_display_media_show_all: Mostrar todo
setting_expand_spoilers: Siempre expandir las publicaciones marcadas con advertencias de contenido setting_expand_spoilers: Siempre expandir las publicaciones marcadas con advertencias de contenido
setting_hide_network: Ocultar tu red setting_hide_network: Ocultar tu red
setting_missing_alt_text_modal: Mostrar cuadro de diálogo de confirmación antes de publicar contenido multimedia sin texto alternativo
setting_reduce_motion: Reducir el movimiento de las animaciones setting_reduce_motion: Reducir el movimiento de las animaciones
setting_system_font_ui: Usar la fuente por defecto del sistema setting_system_font_ui: Usar la fuente por defecto del sistema
setting_system_scrollbars_ui: Usar la barra de desplazamiento por defecto del sistema setting_system_scrollbars_ui: Usar la barra de desplazamiento por defecto del sistema

View file

@ -233,6 +233,7 @@ es:
setting_display_media_show_all: Mostrar todo setting_display_media_show_all: Mostrar todo
setting_expand_spoilers: Siempre expandir las publicaciones marcadas con advertencias de contenido setting_expand_spoilers: Siempre expandir las publicaciones marcadas con advertencias de contenido
setting_hide_network: Ocultar tu red setting_hide_network: Ocultar tu red
setting_missing_alt_text_modal: Mostrar diálogo de confirmación antes de publicar medios sin texto alternativo
setting_reduce_motion: Reducir el movimiento de las animaciones setting_reduce_motion: Reducir el movimiento de las animaciones
setting_system_font_ui: Utilizar la tipografía por defecto del sistema setting_system_font_ui: Utilizar la tipografía por defecto del sistema
setting_system_scrollbars_ui: Utilizar barra de desplazamiento predeterminada del sistema setting_system_scrollbars_ui: Utilizar barra de desplazamiento predeterminada del sistema

View file

@ -233,6 +233,7 @@ fi:
setting_display_media_show_all: Näytä kaikki setting_display_media_show_all: Näytä kaikki
setting_expand_spoilers: Laajenna aina sisältövaroituksilla merkityt julkaisut setting_expand_spoilers: Laajenna aina sisältövaroituksilla merkityt julkaisut
setting_hide_network: Piilota verkostotietosi setting_hide_network: Piilota verkostotietosi
setting_missing_alt_text_modal: Näytä vahvistusikkuna ennen kuin julkaistaan mediaa ilman vaihtoehtoista tekstiä
setting_reduce_motion: Vähennä animaatioiden liikettä setting_reduce_motion: Vähennä animaatioiden liikettä
setting_system_font_ui: Käytä järjestelmän oletusfonttia setting_system_font_ui: Käytä järjestelmän oletusfonttia
setting_system_scrollbars_ui: Käytä järjestelmän oletusarvoista vierityspalkkia setting_system_scrollbars_ui: Käytä järjestelmän oletusarvoista vierityspalkkia

View file

@ -233,6 +233,7 @@ fo:
setting_display_media_show_all: Vís alt setting_display_media_show_all: Vís alt
setting_expand_spoilers: Víðka altíð postar, sum eru merktir við innihaldsávaringum setting_expand_spoilers: Víðka altíð postar, sum eru merktir við innihaldsávaringum
setting_hide_network: Fjal sosiala grafin hjá tær setting_hide_network: Fjal sosiala grafin hjá tær
setting_missing_alt_text_modal: Spyr um góðkenning áðrenn miðlar uttan alternativan tekst verða postaðir
setting_reduce_motion: Minka um rørslu í teknimyndum setting_reduce_motion: Minka um rørslu í teknimyndum
setting_system_font_ui: Brúka vanliga skriftaslagið hjá skipanini setting_system_font_ui: Brúka vanliga skriftaslagið hjá skipanini
setting_system_scrollbars_ui: Brúka vanliga skrullibjálkan hjá skipanini setting_system_scrollbars_ui: Brúka vanliga skrullibjálkan hjá skipanini

View file

@ -3,6 +3,7 @@ fy:
simple_form: simple_form:
hints: hints:
account: account:
attribution_domains: Ien per rigel. Beskermet tsjin falske attribúsjes.
discoverable: Jo iepenbiere berjochten kinne útljochte wurde op ferskate plakken binnen Mastodon en jo account kin oanrekommandearre wurde oan oare brûkers. discoverable: Jo iepenbiere berjochten kinne útljochte wurde op ferskate plakken binnen Mastodon en jo account kin oanrekommandearre wurde oan oare brûkers.
display_name: Jo folsleine namme of in aardige bynamme. display_name: Jo folsleine namme of in aardige bynamme.
fields: Jo website, persoanlike foarnammewurden, leeftiid, alles wat jo mar kwyt wolle. fields: Jo website, persoanlike foarnammewurden, leeftiid, alles wat jo mar kwyt wolle.
@ -155,6 +156,7 @@ fy:
url: Wêrt eveneminten nei ta stjoerd wurde url: Wêrt eveneminten nei ta stjoerd wurde
labels: labels:
account: account:
attribution_domains: Websites dyt jo wurdearring jaan meie
discoverable: Profyl en bydragen yn sykalgoritmen opnimme litte discoverable: Profyl en bydragen yn sykalgoritmen opnimme litte
fields: fields:
name: Label name: Label

View file

@ -233,6 +233,7 @@ gl:
setting_display_media_show_all: Mostrar todo setting_display_media_show_all: Mostrar todo
setting_expand_spoilers: Despregar sempre as publicacións marcadas con avisos de contido setting_expand_spoilers: Despregar sempre as publicacións marcadas con avisos de contido
setting_hide_network: Non mostrar contactos setting_hide_network: Non mostrar contactos
setting_missing_alt_text_modal: Mostrar mensaxe de confirmación antes de publicar multimedia sen texto descritivo
setting_reduce_motion: Reducir o movemento nas animacións setting_reduce_motion: Reducir o movemento nas animacións
setting_system_font_ui: Utilizar a tipografía por defecto do sistema setting_system_font_ui: Utilizar a tipografía por defecto do sistema
setting_system_scrollbars_ui: Usar barras de desprazamento predeterminadas no sistema setting_system_scrollbars_ui: Usar barras de desprazamento predeterminadas no sistema

View file

@ -233,6 +233,7 @@ he:
setting_display_media_show_all: להציג הכול setting_display_media_show_all: להציג הכול
setting_expand_spoilers: להרחיב תמיד הודעות מסומנות באזהרת תוכן setting_expand_spoilers: להרחיב תמיד הודעות מסומנות באזהרת תוכן
setting_hide_network: להחביא את הגרף החברתי שלך setting_hide_network: להחביא את הגרף החברתי שלך
setting_missing_alt_text_modal: הצג כרטיס אישור לפני פרסום קובץ גרפי ללא תיאור מילולי
setting_reduce_motion: הפחתת תנועה בהנפשות setting_reduce_motion: הפחתת תנועה בהנפשות
setting_system_font_ui: להשתמש בגופן ברירת המחדל של המערכת setting_system_font_ui: להשתמש בגופן ברירת המחדל של המערכת
setting_system_scrollbars_ui: להשתמש בפס הגלילה שהוא ברירת המחדל של המערכת setting_system_scrollbars_ui: להשתמש בפס הגלילה שהוא ברירת המחדל של המערכת

View file

@ -230,6 +230,7 @@ hu:
setting_display_media_show_all: Mindent mutat setting_display_media_show_all: Mindent mutat
setting_expand_spoilers: Tartalmi figyelmeztetéssel ellátott bejegyzések automatikus kinyitása setting_expand_spoilers: Tartalmi figyelmeztetéssel ellátott bejegyzések automatikus kinyitása
setting_hide_network: Hálózatod elrejtése setting_hide_network: Hálózatod elrejtése
setting_missing_alt_text_modal: Megerősítési párbeszédablak megjelenítése a helyettesítő szöveg nélküli média közzététele előtt
setting_reduce_motion: Animációk mozgásának csökkentése setting_reduce_motion: Animációk mozgásának csökkentése
setting_system_font_ui: Rendszer betűtípusának használata setting_system_font_ui: Rendszer betűtípusának használata
setting_system_scrollbars_ui: Rendszer alapértelmezett görgetősávjának használata setting_system_scrollbars_ui: Rendszer alapértelmezett görgetősávjának használata

View file

@ -233,6 +233,7 @@ is:
setting_display_media_show_all: Birta allt setting_display_media_show_all: Birta allt
setting_expand_spoilers: Alltaf útfella færslur sem eru með aðvörun vegna efnisins setting_expand_spoilers: Alltaf útfella færslur sem eru með aðvörun vegna efnisins
setting_hide_network: Fela félagsnetið þitt setting_hide_network: Fela félagsnetið þitt
setting_missing_alt_text_modal: Birta staðfestingarglugga áður en myndefni án ALT-hjálpartexta er birt
setting_reduce_motion: Minnka hreyfingu í hreyfimyndum setting_reduce_motion: Minnka hreyfingu í hreyfimyndum
setting_system_font_ui: Nota sjálfgefið letur kerfisins setting_system_font_ui: Nota sjálfgefið letur kerfisins
setting_system_scrollbars_ui: Nota sjálfgefna skrunstiku kerfisins setting_system_scrollbars_ui: Nota sjálfgefna skrunstiku kerfisins

View file

@ -233,6 +233,7 @@ nl:
setting_display_media_show_all: Alles tonen setting_display_media_show_all: Alles tonen
setting_expand_spoilers: Berichten met inhoudswaarschuwingen altijd uitklappen setting_expand_spoilers: Berichten met inhoudswaarschuwingen altijd uitklappen
setting_hide_network: Jouw volgers en wie je volgt verbergen setting_hide_network: Jouw volgers en wie je volgt verbergen
setting_missing_alt_text_modal: Bevestigingsvenster tonen voor het plaatsen van media zonder alt-tekst
setting_reduce_motion: Beweging in animaties verminderen setting_reduce_motion: Beweging in animaties verminderen
setting_system_font_ui: Standaardlettertype van het systeem gebruiken setting_system_font_ui: Standaardlettertype van het systeem gebruiken
setting_system_scrollbars_ui: Standaard scrollbalk van het systeem gebruiken setting_system_scrollbars_ui: Standaard scrollbalk van het systeem gebruiken

View file

@ -231,6 +231,7 @@ nn:
setting_display_media_show_all: Vis alle setting_display_media_show_all: Vis alle
setting_expand_spoilers: Vid alltid ut tut som er merka med innhaldsåtvaringar setting_expand_spoilers: Vid alltid ut tut som er merka med innhaldsåtvaringar
setting_hide_network: Gøym nettverket ditt setting_hide_network: Gøym nettverket ditt
setting_missing_alt_text_modal: Vis stadfestingsdialog før du legg ut media utan alt-tekst
setting_reduce_motion: Minsk rørsle i animasjonar setting_reduce_motion: Minsk rørsle i animasjonar
setting_system_font_ui: Bruk standardskrifttypen på systemet setting_system_font_ui: Bruk standardskrifttypen på systemet
setting_system_scrollbars_ui: Bruk standardrullefeltet til systemet setting_system_scrollbars_ui: Bruk standardrullefeltet til systemet

View file

@ -233,6 +233,7 @@ pt-PT:
setting_display_media_show_all: Mostrar todos setting_display_media_show_all: Mostrar todos
setting_expand_spoilers: Expandir sempre as publicações marcadas com avisos de conteúdo setting_expand_spoilers: Expandir sempre as publicações marcadas com avisos de conteúdo
setting_hide_network: Esconder a tua rede setting_hide_network: Esconder a tua rede
setting_missing_alt_text_modal: Mostrar janela de confirmação antes de publicar elementos gráficos sem texto alternativo
setting_reduce_motion: Reduzir movimento em animações setting_reduce_motion: Reduzir movimento em animações
setting_system_font_ui: Usar o tipo de letra padrão do sistema setting_system_font_ui: Usar o tipo de letra padrão do sistema
setting_system_scrollbars_ui: Utilizar a barra de deslocação predefinida do sistema setting_system_scrollbars_ui: Utilizar a barra de deslocação predefinida do sistema

View file

@ -232,6 +232,7 @@ sq:
setting_display_media_show_all: Shfaqi krejt setting_display_media_show_all: Shfaqi krejt
setting_expand_spoilers: Mesazhet me sinjalizime mbi lëndën, zgjeroji përherë setting_expand_spoilers: Mesazhet me sinjalizime mbi lëndën, zgjeroji përherë
setting_hide_network: Fshiheni rrjetin tuaj setting_hide_network: Fshiheni rrjetin tuaj
setting_missing_alt_text_modal: Shfaq dialog ripohimi, para postimi mediash pa tekst alternativ
setting_reduce_motion: Zvogëlo lëvizjen në animacione setting_reduce_motion: Zvogëlo lëvizjen në animacione
setting_system_font_ui: Përdor shkronja parazgjedhje të sistemit setting_system_font_ui: Përdor shkronja parazgjedhje të sistemit
setting_system_scrollbars_ui: Përdor shtyllë rrëshqitjesh parazgjedhje të sistemit setting_system_scrollbars_ui: Përdor shtyllë rrëshqitjesh parazgjedhje të sistemit

View file

@ -233,6 +233,7 @@ tr:
setting_display_media_show_all: Tümünü göster setting_display_media_show_all: Tümünü göster
setting_expand_spoilers: İçerik uyarılarıyla işaretli gönderileri her zaman genişlet setting_expand_spoilers: İçerik uyarılarıyla işaretli gönderileri her zaman genişlet
setting_hide_network: Sosyal grafiğini gizle setting_hide_network: Sosyal grafiğini gizle
setting_missing_alt_text_modal: Alternatif metni olmayan medya göndermeden önce onay sorusu göster
setting_reduce_motion: Animasyonlarda hareketi azalt setting_reduce_motion: Animasyonlarda hareketi azalt
setting_system_font_ui: Sistemin varsayılan yazı tipini kullan setting_system_font_ui: Sistemin varsayılan yazı tipini kullan
setting_system_scrollbars_ui: Sistemin varsayılan kaydırma çubuğunu kullan setting_system_scrollbars_ui: Sistemin varsayılan kaydırma çubuğunu kullan

View file

@ -233,6 +233,7 @@ zh-CN:
setting_display_media_show_all: 显示全部 setting_display_media_show_all: 显示全部
setting_expand_spoilers: 一律展开具有内容警告的嘟文 setting_expand_spoilers: 一律展开具有内容警告的嘟文
setting_hide_network: 隐藏你的社交网络 setting_hide_network: 隐藏你的社交网络
setting_missing_alt_text_modal: 发布媒体时若未为其设置替代文本,则显示确认对话框
setting_reduce_motion: 降低过渡动画效果 setting_reduce_motion: 降低过渡动画效果
setting_system_font_ui: 使用系统默认字体 setting_system_font_ui: 使用系统默认字体
setting_system_scrollbars_ui: 使用系统默认样式的滚动条 setting_system_scrollbars_ui: 使用系统默认样式的滚动条

View file

@ -233,6 +233,7 @@ zh-TW:
setting_display_media_show_all: 全部顯示 setting_display_media_show_all: 全部顯示
setting_expand_spoilers: 永遠展開標有內容警告的嘟文 setting_expand_spoilers: 永遠展開標有內容警告的嘟文
setting_hide_network: 隱藏您的社交網路 setting_hide_network: 隱藏您的社交網路
setting_missing_alt_text_modal: 發表未包含說明文字之多媒體嘟文前先詢問我
setting_reduce_motion: 減少過渡動畫效果 setting_reduce_motion: 減少過渡動畫效果
setting_system_font_ui: 使用系統預設字型 setting_system_font_ui: 使用系統預設字型
setting_system_scrollbars_ui: 使用系統預設捲動軸 setting_system_scrollbars_ui: 使用系統預設捲動軸

View file

@ -1,68 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::EmailDomainBlocksController do
render_views
before do
sign_in Fabricate(:admin_user), scope: :user
end
describe 'GET #index' do
around do |example|
default_per_page = EmailDomainBlock.default_per_page
EmailDomainBlock.paginates_per 2
example.run
EmailDomainBlock.paginates_per default_per_page
end
it 'returns http success' do
2.times { Fabricate(:email_domain_block) }
Fabricate(:email_domain_block, allow_with_approval: true)
get :index, params: { page: 2 }
expect(response).to have_http_status(200)
end
end
describe 'GET #new' do
it 'returns http success' do
get :new
expect(response).to have_http_status(200)
end
end
describe 'POST #create' do
context 'when resolve button is pressed' do
before do
resolver = instance_double(Resolv::DNS)
allow(resolver).to receive(:getresources)
.with('example.com', Resolv::DNS::Resource::IN::MX)
.and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
post :create, params: { email_domain_block: { domain: 'example.com' } }
end
it 'renders new template' do
expect(response).to render_template(:new)
end
end
context 'when save button is pressed' do
before do
post :create, params: { email_domain_block: { domain: 'example.com' }, save: '' }
end
it 'blocks the domain and redirects to email domain blocks' do
expect(EmailDomainBlock.find_by(domain: 'example.com')).to_not be_nil
expect(response).to redirect_to(admin_email_domain_blocks_path)
end
end
end
end

View file

@ -60,16 +60,12 @@ RSpec.describe Request do
expect(a_request(:get, 'http://example.com')).to have_been_made.once expect(a_request(:get, 'http://example.com')).to have_been_made.once
end end
it 'sets headers' do it 'makes a request with expected headers, yields, and closes the underlying connection' do
expect { |block| subject.perform(&block) }.to yield_control
expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
end
it 'closes underlying connection' do
allow(subject.send(:http_client)).to receive(:close) allow(subject.send(:http_client)).to receive(:close)
expect { |block| subject.perform(&block) }.to yield_control expect { |block| subject.perform(&block) }.to yield_control
expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
expect(subject.send(:http_client)).to have_received(:close) expect(subject.send(:http_client)).to have_received(:close)
end end
@ -80,6 +76,29 @@ RSpec.describe Request do
end end
end end
context 'with a redirect and HTTP signatures' do
let(:account) { Fabricate(:account) }
before do
stub_request(:get, 'http://example.com').to_return(status: 301, headers: { Location: 'http://redirected.example.com/foo' })
stub_request(:get, 'http://redirected.example.com/foo').to_return(body: 'lorem ipsum')
end
it 'makes a request with expected headers and follows redirects' do
expect { |block| subject.on_behalf_of(account).perform(&block) }.to yield_control
# request.headers includes the `Signature` sent for the first request
expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made.once
# request.headers includes the `Signature`, but it has changed
expect(a_request(:get, 'http://redirected.example.com/foo').with(headers: subject.headers.merge({ 'Host' => 'redirected.example.com' }))).to_not have_been_made
# `with(headers: )` matching tests for inclusion, so strip `Signature`
# This doesn't actually test that there is a signature, but it tests that the original signature is not passed
expect(a_request(:get, 'http://redirected.example.com/foo').with(headers: subject.headers.without('Signature').merge({ 'Host' => 'redirected.example.com' }))).to have_been_made.once
end
end
context 'with private host' do context 'with private host' do
around do |example| around do |example|
WebMock.disable! WebMock.disable!

View file

@ -59,7 +59,7 @@ RSpec.describe 'Instances' do
description_limit: MediaAttachment::MAX_DESCRIPTION_LENGTH description_limit: MediaAttachment::MAX_DESCRIPTION_LENGTH
), ),
polls: include( polls: include(
max_options: PollValidator::MAX_OPTIONS max_options: PollOptionsValidator::MAX_OPTIONS
) )
) )
) )

View file

@ -153,7 +153,7 @@ RSpec.describe 'Notifications' do
it 'returns a notification group covering all notifications' do it 'returns a notification group covering all notifications' do
subject subject
notification_ids = user.account.notifications.reload.pluck(:id) notification_ids = user.account.notifications.order(id: :asc).pluck(:id)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response.content_type) expect(response.content_type)
@ -175,7 +175,7 @@ RSpec.describe 'Notifications' do
it 'returns a notification group covering all notifications' do it 'returns a notification group covering all notifications' do
subject subject
notification_ids = user.account.notifications.reload.pluck(:id) notification_ids = user.account.notifications.order(id: :asc).pluck(:id)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response.content_type) expect(response.content_type)

View file

@ -7,31 +7,69 @@ RSpec.describe PrecomputeFeedService do
describe 'call' do describe 'call' do
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
let!(:list) { Fabricate(:list, account: account, exclusive: false) }
it 'fills a user timeline with statuses' do context 'when no eligible status exist' do
account = Fabricate(:account) it 'raises no error and results in an empty timeline' do
status = Fabricate(:status, account: account)
subject.call(account)
expect(redis.zscore(FeedManager.instance.key(:home, account.id), status.id)).to be_within(0.1).of(status.id.to_f)
end
it 'does not raise an error even if it could not find any status' do
account = Fabricate(:account)
expect { subject.call(account) }.to_not raise_error expect { subject.call(account) }.to_not raise_error
expect(redis.zcard(FeedManager.instance.key(:home, account.id))).to eq(0)
end
end end
it 'filters statuses' do context 'with eligible statuses' do
account = Fabricate(:account) let(:muted_account) { Fabricate(:account) }
muted_account = Fabricate(:account) let!(:followed_account) { Fabricate(:account) }
Fabricate(:mute, account: account, target_account: muted_account) let!(:requested_account) { Fabricate(:account) }
reblog = Fabricate(:status, account: muted_account) let!(:own_status) { Fabricate(:status, account: account) }
Fabricate(:status, account: account, reblog: reblog) let!(:followed_status) { Fabricate(:status, account: followed_account) }
let!(:unreadable_dm_from_followed) { Fabricate(:status, account: followed_account, visibility: :direct) }
let!(:requested_status) { Fabricate(:status, account: requested_account) }
let!(:muted_status) { Fabricate(:status, account: muted_account) }
let!(:muted_reblog) { Fabricate(:status, account: followed_account, reblog: muted_status) }
let!(:known_reply) { Fabricate(:status, account: followed_account, in_reply_to_id: own_status.id) }
let!(:unknown_reply) { Fabricate(:status, account: followed_account, in_reply_to_id: requested_status.id) }
before do
account.follow!(followed_account)
account.request_follow!(requested_account)
account.mute!(muted_account)
AddAccountsToListService.new.call(list, [followed_account])
end
it "fills a user's home and list timelines with the expected posts" do
subject.call(account) subject.call(account)
expect(redis.zscore(FeedManager.instance.key(:home, account.id), reblog.id)).to be_nil home_timeline_ids = redis.zrevrangebyscore(FeedManager.instance.key(:home, account.id), '(+inf', '(-inf', limit: [0, 30], with_scores: true).map { |id| id.first.to_i }
list_timeline_ids = redis.zrevrangebyscore(FeedManager.instance.key(:list, list.id), '(+inf', '(-inf', limit: [0, 30], with_scores: true).map { |id| id.first.to_i }
expect(home_timeline_ids).to include(
own_status.id,
followed_status.id,
known_reply.id
)
expect(list_timeline_ids).to include(
followed_status.id
)
expect(home_timeline_ids).to_not include(
requested_status.id,
unknown_reply.id,
unreadable_dm_from_followed.id,
muted_status.id,
muted_reblog.id
)
expect(list_timeline_ids).to_not include(
requested_status.id,
unknown_reply.id,
unreadable_dm_from_followed.id,
muted_status.id,
muted_reblog.id
)
end
end end
end end
end end

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