Compare commits

...

14 Commits

Author SHA1 Message Date
Mischa Holz
a86e1afb7e
Merge a04273b06d into 07912a1cb7 2025-07-15 16:03:56 +00:00
diondiondion
07912a1cb7
Update age limit wording (#35387) 2025-07-15 15:46:44 +00:00
Claire
d36bf3b6fb
Fix support for quote verification in implicit status updates (#35384) 2025-07-15 15:36:12 +00:00
Claire
594976a538
Refactor ActivityPub::Activity::Accept and ActivityPub::Activity::Reject specs (#35382) 2025-07-15 13:18:37 +00:00
Matt Jankowski
0efb889a9c
Extract constant for attribution domains limit in account (#35350) 2025-07-15 13:08:24 +00:00
Claire
c0eabe289b
Always give local quote of remote posts a quote request URI (#35383) 2025-07-15 13:01:03 +00:00
Claire
5bbc3c5ebb
Fix quoteAuthorization type in JSON-LD context (#35380) 2025-07-15 09:32:02 +00:00
github-actions[bot]
d5e2cf5d3c
New Crowdin Translations (automated) (#35379)
Some checks failed
Check i18n / check-i18n (push) Waiting to run
Chromatic / Run Chromatic (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
CSS Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.2) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
JavaScript Linting / lint (push) Has been cancelled
JavaScript Testing / test (push) Has been cancelled
Co-authored-by: GitHub Actions <noreply@github.com>
2025-07-15 08:16:56 +00:00
diondiondion
82a6ff091f
fix: Improve Dropdown component accessibility (#35373) 2025-07-15 07:52:34 +00:00
Mischa Holz
a04273b06d
Merge branch 'main' into emoji-mentions 2025-01-23 11:22:31 +01:00
Mischa Holz
efffde0230
Add semicolon 2024-11-29 20:50:45 +01:00
Mischa Holz
fd4c869f63
Replace the domain part of the mention regex with twitter's validDomain 2024-11-29 20:50:43 +01:00
Mischa Holz
e7d0e511a9
Add a test for unicode domain name mentions 2024-11-29 20:50:41 +01:00
Mischa Holz
1b909c98ab
Use TwitterText regex for valid domains in mentions
TwitterText already has a regex to check for valid domains that allows a
few domains the current Regex did not allow. Mainly it allows mentions
of accounts on domains with emoji in their domain names to be processed
correctly.

Mentions like `@test@🌈🌈🌈.st` will now work correctly.
2024-11-29 20:50:32 +01:00
60 changed files with 428 additions and 431 deletions

View File

@ -30,7 +30,7 @@ module ContextHelper
'quote' => 'https://w3id.org/fep/044f#quote',
'quoteUri' => 'http://fedibird.com/ns#quoteUri',
'_misskey_quote' => 'https://misskey-hub.net/ns#_misskey_quote',
'quoteAuthorization' => 'https://w3id.org/fep/044f#quoteAuthorization',
'quoteAuthorization' => { '@id' => 'https://w3id.org/fep/044f#quoteAuthorization', '@type' => '@id' },
},
interaction_policies: {
'gts' => 'https://gotosocial.org/ns#',

View File

@ -5,6 +5,7 @@ import {
useCallback,
cloneElement,
Children,
useId,
} from 'react';
import classNames from 'classnames';
@ -16,6 +17,7 @@ import Overlay from 'react-overlays/Overlay';
import type {
OffsetValue,
UsePopperOptions,
Placement,
} from 'react-overlays/esm/usePopper';
import { fetchRelationships } from 'mastodon/actions/accounts';
@ -295,6 +297,11 @@ interface DropdownProps<Item = MenuItem> {
title?: string;
disabled?: boolean;
scrollable?: boolean;
placement?: Placement;
/**
* Prevent the `ScrollableList` with this scrollKey
* from being scrolled while the dropdown is open
*/
scrollKey?: string;
status?: ImmutableMap<string, unknown>;
forceDropdown?: boolean;
@ -316,6 +323,7 @@ export const Dropdown = <Item = MenuItem,>({
title = 'Menu',
disabled,
scrollable,
placement = 'bottom',
status,
forceDropdown = false,
renderItem,
@ -331,16 +339,15 @@ export const Dropdown = <Item = MenuItem,>({
);
const [currentId] = useState(id++);
const open = currentId === openDropdownId;
const activeElement = useRef<HTMLElement | null>(null);
const targetRef = useRef<HTMLButtonElement | null>(null);
const buttonRef = useRef<HTMLButtonElement | null>(null);
const menuId = useId();
const prefetchAccountId = status
? status.getIn(['account', 'id'])
: undefined;
const handleClose = useCallback(() => {
if (activeElement.current) {
activeElement.current.focus({ preventScroll: true });
activeElement.current = null;
if (buttonRef.current) {
buttonRef.current.focus({ preventScroll: true });
}
dispatch(
@ -375,7 +382,7 @@ export const Dropdown = <Item = MenuItem,>({
[handleClose, onItemClick, items],
);
const handleClick = useCallback(
const toggleDropdown = useCallback(
(e: React.MouseEvent | React.KeyboardEvent) => {
const { type } = e;
@ -423,38 +430,6 @@ export const Dropdown = <Item = MenuItem,>({
],
);
const handleMouseDown = useCallback(() => {
if (!open && document.activeElement instanceof HTMLElement) {
activeElement.current = document.activeElement;
}
}, [open]);
const handleButtonKeyDown = useCallback(
(e: React.KeyboardEvent) => {
switch (e.key) {
case ' ':
case 'Enter':
handleMouseDown();
break;
}
},
[handleMouseDown],
);
const handleKeyPress = useCallback(
(e: React.KeyboardEvent) => {
switch (e.key) {
case ' ':
case 'Enter':
handleClick(e);
e.stopPropagation();
e.preventDefault();
break;
}
},
[handleClick],
);
useEffect(() => {
return () => {
if (currentId === openDropdownId) {
@ -465,14 +440,16 @@ export const Dropdown = <Item = MenuItem,>({
let button: React.ReactElement;
const buttonProps = {
disabled,
onClick: toggleDropdown,
'aria-expanded': open,
'aria-controls': menuId,
ref: buttonRef,
};
if (children) {
button = cloneElement(Children.only(children), {
onClick: handleClick,
onMouseDown: handleMouseDown,
onKeyDown: handleButtonKeyDown,
onKeyPress: handleKeyPress,
ref: targetRef,
});
button = cloneElement(Children.only(children), buttonProps);
} else if (icon && iconComponent) {
button = (
<IconButton
@ -480,12 +457,7 @@ export const Dropdown = <Item = MenuItem,>({
iconComponent={iconComponent}
title={title}
active={open}
disabled={disabled}
onClick={handleClick}
onMouseDown={handleMouseDown}
onKeyDown={handleButtonKeyDown}
onKeyPress={handleKeyPress}
ref={targetRef}
{...buttonProps}
/>
);
} else {
@ -499,13 +471,13 @@ export const Dropdown = <Item = MenuItem,>({
<Overlay
show={open}
offset={offset}
placement='bottom'
placement={placement}
flip
target={targetRef}
target={buttonRef}
popperConfig={popperConfig}
>
{({ props, arrowProps, placement }) => (
<div {...props}>
<div {...props} id={menuId}>
<div className={`dropdown-animation dropdown-menu ${placement}`}>
<div
className={`dropdown-menu__arrow ${placement}`}

View File

@ -14,7 +14,6 @@ interface Props {
onClick?: React.MouseEventHandler<HTMLButtonElement>;
onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>;
active?: boolean;
expanded?: boolean;
style?: React.CSSProperties;
@ -45,7 +44,6 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
activeStyle,
onClick,
onKeyDown,
onKeyPress,
onMouseDown,
active = false,
disabled = false,
@ -85,16 +83,6 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
[disabled, onClick],
);
const handleKeyPress: React.KeyboardEventHandler<HTMLButtonElement> =
useCallback(
(e) => {
if (!disabled) {
onKeyPress?.(e);
}
},
[disabled, onKeyPress],
);
const handleMouseDown: React.MouseEventHandler<HTMLButtonElement> =
useCallback(
(e) => {
@ -161,7 +149,6 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
onClick={handleClick}
onMouseDown={handleMouseDown}
onKeyDown={handleKeyDown}
onKeyPress={handleKeyPress} // eslint-disable-line @typescript-eslint/no-deprecated
style={buttonStyle}
tabIndex={tabIndex}
disabled={disabled}

View File

@ -1,9 +1,20 @@
import regexSupplant from 'twitter-text/dist/lib/regexSupplant';
import validDomain from 'twitter-text/dist/regexp/validDomain';
import { urlRegex } from './url_regex';
const urlPlaceholder = '$2xxxxxxxxxxxxxxxxxxxxxxx';
const validMention = regexSupplant(
'(^|[^/\\w])@(([a-z0-9_]+)@(#{validDomain}))',
{
validDomain,
},
'ig'
);
export function countableText(inputText) {
return inputText
.replace(urlRegex, urlPlaceholder)
.replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '$1@$3');
.replace(validMention, '$1@$3');
}

View File

@ -50,16 +50,22 @@ export const MoreLink: React.FC = () => {
const menu = useMemo(() => {
const arr: MenuItem[] = [
{ text: intl.formatMessage(messages.filters), href: '/filters' },
{ text: intl.formatMessage(messages.mutes), to: '/mutes' },
{ text: intl.formatMessage(messages.blocks), to: '/blocks' },
{
text: intl.formatMessage(messages.domainBlocks),
to: '/domain_blocks',
href: '/filters',
text: intl.formatMessage(messages.filters),
},
{
to: '/mutes',
text: intl.formatMessage(messages.mutes),
},
{
to: '/blocks',
text: intl.formatMessage(messages.blocks),
},
{
to: '/domain_blocks',
text: intl.formatMessage(messages.domainBlocks),
},
];
arr.push(
null,
{
href: '/settings/privacy',
@ -77,7 +83,7 @@ export const MoreLink: React.FC = () => {
href: '/settings/export',
text: intl.formatMessage(messages.importExport),
},
);
];
if (canManageReports(permissions)) {
arr.push(null, {
@ -106,7 +112,7 @@ export const MoreLink: React.FC = () => {
}, [intl, dispatch, permissions]);
return (
<Dropdown items={menu}>
<Dropdown items={menu} placement='bottom-start'>
<button className='column-link column-link--transparent'>
<Icon id='' icon={MoreHorizIcon} className='column-link__icon' />

View File

@ -62,7 +62,7 @@
"account.mute_notifications_short": "Sluk for notifikationer",
"account.mute_short": "Skjul",
"account.muted": "Skjult",
"account.muting": "Tavsgørelse",
"account.muting": "Skjuler",
"account.mutual": "I følger hinanden",
"account.no_bio": "Ingen beskrivelse til rådighed.",
"account.open_original_page": "Åbn oprindelig side",

View File

@ -795,7 +795,7 @@
"report.thanks.title_actionable": "Gracias por informar, estudiaremos esto.",
"report.unfollow": "Dejar de seguir a @{name}",
"report.unfollow_explanation": "Estás siguiendo esta cuenta. Para dejar de ver sus publicaciones en tu página de inicio, deja de seguirla.",
"report_notification.attached_statuses": "{count, plural, one {{count} publicación} other {{count} publicaciones}} adjunta(s)",
"report_notification.attached_statuses": "{count, plural, one {{count} publicación adjunta} other {{count} publicaciones adjuntas}}",
"report_notification.categories.legal": "Legal",
"report_notification.categories.legal_sentence": "contenido ilegal",
"report_notification.categories.other": "Otros",

View File

@ -4,17 +4,17 @@
"about.default_locale": "по умолчанию",
"about.disclaimer": "Mastodon — свободное программное обеспечение с открытым исходным кодом и торговая марка Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Причина не указана",
"about.domain_blocks.preamble": "Mastodon обычно позволяет просматривать содержимое и взаимодействовать с пользователями любых других серверов в федивёрсе. Вот исключения, сделанные конкретно для этого сервера.",
"about.domain_blocks.silenced.explanation": "Как правило, вы не увидите профили и контент с этого сервера, если специально не будете их искать или не подпишетесь на них.",
"about.domain_blocks.preamble": "Обычно Mastodon даёт вам возможность просматривать содержимое с любых других серверов в федивёрсе и взаимодействовать с их пользователями. Вот исключения, сделанные конкретно для этого сервера:",
"about.domain_blocks.silenced.explanation": "Как правило, вы не увидите профили и содержимое с этого сервера, если только вы не запросите их с помощью поиска или не подпишетесь на пользователя с этого сервера.",
"about.domain_blocks.silenced.title": "Ограничивается",
"about.domain_blocks.suspended.explanation": "Никакие данные с этого сервера не будут обрабатываться, храниться или обмениваться, что делает невозможным любое взаимодействие или связь с пользователями с этого сервера.",
"about.domain_blocks.suspended.explanation": "Никакие данные с этого сервера не будут обрабатываться, храниться и обмениваться, что делает невозможным любое взаимодействие и связь с пользователями на этом сервере.",
"about.domain_blocks.suspended.title": "Заблокирован",
"about.language_label": "Язык",
"about.not_available": "Эта информация не указана на данном сервере.",
"about.not_available": "Администраторы сервера предпочли не раскрывать эту информацию.",
"about.powered_by": "Децентрализованная социальная сеть на базе {mastodon}",
"about.rules": "Правила сервера",
"account.account_note_header": "Личная заметка",
"account.add_or_remove_from_list": "Управление списками",
"account.add_or_remove_from_list": "Добавить в списки",
"account.badges.bot": "Бот",
"account.badges.group": "Группа",
"account.block": "Заблокировать @{name}",
@ -25,10 +25,10 @@
"account.cancel_follow_request": "Отозвать запрос на подписку",
"account.copy": "Копировать ссылку на профиль",
"account.direct": "Упомянуть @{name} лично",
"account.disable_notifications": "Не уведомлять о постах от @{name}",
"account.disable_notifications": "Не уведомлять о постах пользователя @{name}",
"account.domain_blocking": "Домен заблокирован",
"account.edit_profile": "Редактировать",
"account.enable_notifications": "Уведомлять о постах от @{name}",
"account.enable_notifications": "Уведомлять о постах пользователя @{name}",
"account.endorse": "Рекомендовать в профиле",
"account.familiar_followers_many": "В подписках у {name1}, {name2}, и ещё {othersCount, plural, one {# человека, которого вы знаете} other {# человек, которых вы знаете}}",
"account.familiar_followers_one": "В подписках у {name1}",
@ -36,7 +36,7 @@
"account.featured": "Рекомендации",
"account.featured.accounts": "Профили",
"account.featured.hashtags": "Хештеги",
"account.featured_tags.last_status_at": "Последний пост {date}",
"account.featured_tags.last_status_at": "Последний пост опубликован {date}",
"account.featured_tags.last_status_never": "Нет постов",
"account.follow": "Подписаться",
"account.follow_back": "Подписаться в ответ",
@ -59,7 +59,7 @@
"account.mention": "Упомянуть @{name}",
"account.moved_to": "У {name} теперь новая учётная запись:",
"account.mute": "Игнорировать @{name}",
"account.mute_notifications_short": "Отключить уведомления",
"account.mute_notifications_short": "Скрыть уведомления",
"account.mute_short": "Игнорировать",
"account.muted": "Игнорируется",
"account.muting": "Игнорируется",
@ -83,7 +83,7 @@
"account.unendorse": "Не рекомендовать в профиле",
"account.unfollow": "Отписаться",
"account.unmute": "Не игнорировать @{name}",
"account.unmute_notifications_short": "Включить уведомления",
"account.unmute_notifications_short": "Показать уведомления",
"account.unmute_short": "Не игнорировать",
"account_note.placeholder": "Текст заметки",
"admin.dashboard.daily_retention": "Уровень удержания пользователей после регистрации, в днях",
@ -129,10 +129,10 @@
"annual_report.summary.thanks": "Спасибо за то, что были вместе с Mastodon!",
"attachments_list.unprocessed": "(не обработан)",
"audio.hide": "Скрыть аудио",
"block_modal.remote_users_caveat": "Мы попросим сервер {domain} уважать ваше решение, однако соблюдение им блокировки не гарантировано, поскольку некоторые серверы могут по-разному обрабатывать запросы. Публичные посты по-прежнему могут быть видны неавторизованным пользователям.",
"block_modal.remote_users_caveat": "Мы попросим сервер {domain} уважать ваше решение, однако нельзя гарантировать, что он будет соблюдать блокировку, поскольку некоторые серверы могут по-разному обрабатывать запросы. Публичные посты по-прежнему могут быть видны неавторизованным пользователям.",
"block_modal.show_less": "Показать меньше",
"block_modal.show_more": "Показать больше",
"block_modal.they_cant_mention": "Он не сможет упоминать вас или подписаться на вас.",
"block_modal.they_cant_mention": "Он не сможет ни упоминать вас, ни подписаться на вас.",
"block_modal.they_cant_see_posts": "Он не сможет видеть ваши посты, а вы не будете видеть его посты.",
"block_modal.they_will_know": "Он будет знать, что вы его блокируете.",
"block_modal.title": "Заблокировать пользователя?",
@ -141,9 +141,9 @@
"boost_modal.reblog": "Продвинуть пост?",
"boost_modal.undo_reblog": "Отменить продвижение?",
"bundle_column_error.copy_stacktrace": "Копировать отчёт об ошибке",
"bundle_column_error.error.body": "Запрошенная страница не может быть отображена. Это может быть вызвано ошибкой в нашем коде или проблемой совместимости браузера.",
"bundle_column_error.error.body": "Запрошенная страница не может быть отображена. Это могло произойти из-за ошибки в нашем коде или из-за проблемы совместимости браузера.",
"bundle_column_error.error.title": "О нет!",
"bundle_column_error.network.body": "При загрузке этой страницы произошла ошибка. Это может быть вызвано временными проблемами с вашим подключением к интернету или ошибкой на сервере.",
"bundle_column_error.network.body": "При загрузке этой страницы произошла ошибка. Она могла быть вызвана временными проблемами либо с вашим подключением к интернету, либо с этим сервером.",
"bundle_column_error.network.title": "Ошибка сети",
"bundle_column_error.retry": "Попробовать снова",
"bundle_column_error.return": "Вернуться на главную",
@ -152,10 +152,10 @@
"bundle_modal_error.close": "Закрыть",
"bundle_modal_error.message": "Кое-что пошло не так при загрузке этой страницы.",
"bundle_modal_error.retry": "Попробовать снова",
"closed_registrations.other_server_instructions": "Поскольку Mastodon децентрализован, вы можете зарегистрироваться на другом сервере и всё равно взаимодействовать с этим сервером.",
"closed_registrations.other_server_instructions": "Благодаря тому что Mastodon децентрализован, вы можете взаимодействовать с этим сервером, даже если зарегистрируетесь на другом сервере.",
"closed_registrations_modal.description": "Зарегистрироваться на {domain} сейчас не выйдет, но имейте в виду, что вам не нужна учётная запись именно на {domain}, чтобы использовать Mastodon.",
"closed_registrations_modal.find_another_server": "Найти другой сервер",
"closed_registrations_modal.preamble": "Mastodon децентрализован, поэтому независимо от того, где именно вы зарегистрируетесь, вы сможете подписываться и взаимодействовать с кем угодно на этом сервере. Вы даже можете создать свой собственный сервер!",
"closed_registrations_modal.preamble": "Mastodon децентрализован, поэтому независимо от того, где именно вы зарегистрируетесь, вы сможете подписываться на кого угодно и взаимодействовать с кем угодно на этом сервере. Вы даже можете создать свой собственный сервер!",
"closed_registrations_modal.title": "Регистрация в Mastodon",
"column.about": "О проекте",
"column.blocks": "Заблокированные пользователи",
@ -170,7 +170,7 @@
"column.firehose": "Живая лента",
"column.follow_requests": "Запросы на подписку",
"column.home": "Главная",
"column.list_members": "Добавить или удалить из списка",
"column.list_members": "Пользователи в списке",
"column.lists": "Списки",
"column.mutes": "Игнорируемые пользователи",
"column.notifications": "Уведомления",
@ -240,7 +240,7 @@
"confirmations.missing_alt_text.title": "Добавить альтернативный текст?",
"confirmations.mute.confirm": "Игнорировать",
"confirmations.redraft.confirm": "Удалить и исправить",
"confirmations.redraft.message": "Вы уверены, что хотите удалить этот пост и создать его заново? Взаимодействия, такие как добавление в избранное или продвижение, будут потеряны, а ответы к оригинальному посту перестанут на него ссылаться.",
"confirmations.redraft.message": "Вы уверены, что хотите удалить этот пост и создать его заново? Взаимодействия, такие как добавление в избранное и продвижение, будут потеряны, а ответы к оригинальному посту перестанут на него ссылаться.",
"confirmations.redraft.title": "Удалить и создать пост заново?",
"confirmations.remove_from_followers.confirm": "Убрать подписчика",
"confirmations.remove_from_followers.message": "Пользователь {name} перестанет быть подписан на вас. Продолжить?",
@ -275,9 +275,9 @@
"domain_block_modal.title": "Заблокировать домен?",
"domain_block_modal.you_will_lose_num_followers": "Вы потеряете {followersCount, plural, one {{followersCountDisplay} подписчика} few {{followersCountDisplay} подписчика} other {{followersCountDisplay} подписчиков}} и {followingCount, plural, one {{followingCountDisplay} подписку} few {{followingCountDisplay} подписки} other {{followingCountDisplay} подписок}}.",
"domain_block_modal.you_will_lose_relationships": "Вы потеряете все подписки и всех подписчиков с этого сервера.",
"domain_block_modal.you_wont_see_posts": "Вы не будете видеть посты или уведомления от пользователей с этого сервера.",
"domain_block_modal.you_wont_see_posts": "Вы не будете видеть посты и уведомления от пользователей с этого сервера.",
"domain_pill.activitypub_lets_connect": "Благодаря ему вы можете связываться и взаимодействовать не только с пользователями Mastodon, но и с пользователями других платформ.",
"domain_pill.activitypub_like_language": "ActivityPub это язык, на котором Mastodon говорит с другими социальными сетями.",
"domain_pill.activitypub_like_language": "ActivityPub это язык, на котором Mastodon говорит с другими социальными сетями.",
"domain_pill.server": "Сервер",
"domain_pill.their_handle": "Адрес пользователя:",
"domain_pill.their_server": "Цифровой дом пользователя, где находятся все его посты.",
@ -319,7 +319,7 @@
"empty_column.direct": "Вы ещё не упоминали кого-либо и сами не были ни разу упомянуты лично. Все личные упоминания будут показаны здесь.",
"empty_column.domain_blocks": "Заблокированных доменов пока нет.",
"empty_column.explore_statuses": "Сейчас нет популярных постов. Проверьте позже!",
"empty_column.favourited_statuses": "Вы ещё не добавили ни один пост в избранное. Все добавленные вами в избранное посты будут показаны здесь.",
"empty_column.favourited_statuses": "Вы ещё не добавили ни одного поста в избранное. Все добавленные вами в избранное посты будут показаны здесь.",
"empty_column.favourites": "Никто ещё не добавил этот пост в избранное. Все пользователи, добавившие этот пост в избранное, будут показаны здесь.",
"empty_column.follow_requests": "Вам ещё не приходили запросы на подписку. Все новые запросы будут показаны здесь.",
"empty_column.followed_tags": "Вы ещё не подписались ни на один хештег. Все хештеги, на которые вы подписаны, будут показаны здесь.",
@ -346,7 +346,7 @@
"featured_carousel.post": "Пост",
"featured_carousel.previous": "Предыдущий",
"featured_carousel.slide": "{index} из {total}",
"filter_modal.added.context_mismatch_explanation": "Этот фильтр не применяется в том контексте, в котором вы видели этот пост. Если вы хотите, чтобы пост был отфильтрован в этом контексте, необходимо редактировать фильтр.",
"filter_modal.added.context_mismatch_explanation": "Этот фильтр не применяется в том контексте, в котором вы видели этот пост. Если вы хотите, чтобы пост был отфильтрован в текущем контексте, необходимо редактировать фильтр.",
"filter_modal.added.context_mismatch_title": "Несоответствие контекста!",
"filter_modal.added.expired_explanation": "Этот фильтр истёк. Чтобы он был применён, вам нужно изменить срок действия фильтра.",
"filter_modal.added.expired_title": "Истёкший фильтр!",
@ -413,18 +413,18 @@
"hashtag.counter_by_uses": "{count, plural, one {{counter} пост} few {{counter} поста} other {{counter} постов}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} пост} few {{counter} поста} other {{counter} постов}} сегодня",
"hashtag.feature": "Рекомендовать в профиле",
"hashtag.follow": "Подписаться на новые посты",
"hashtag.follow": "Подписаться на хештег",
"hashtag.mute": "Игнорировать #{hashtag}",
"hashtag.unfeature": "Не рекомендовать в профиле",
"hashtag.unfollow": "Отписаться от новых постов",
"hashtag.unfollow": "Отписаться от хештега",
"hashtags.and_other": "…и {count, plural, other {ещё #}}",
"hints.profiles.followers_may_be_missing": "Некоторые подписчики этого профиля могут отсутствовать.",
"hints.profiles.follows_may_be_missing": "Некоторые подписки этого профиля могут отсутствовать.",
"hints.profiles.posts_may_be_missing": "Некоторые посты в этом профиле могут отсутствовать.",
"hints.profiles.followers_may_be_missing": "Некоторые подписчики этого профиля могут здесь отсутствовать.",
"hints.profiles.follows_may_be_missing": "Некоторые подписки этого профиля могут здесь отсутствовать.",
"hints.profiles.posts_may_be_missing": "Некоторые посты в этом профиле могут здесь отсутствовать.",
"hints.profiles.see_more_followers": "Перейдите на {domain}, чтобы увидеть всех подписчиков",
"hints.profiles.see_more_follows": "Перейдите на {domain}, чтобы увидеть все подписки",
"hints.profiles.see_more_posts": "Перейдите на {domain}, чтобы увидеть все посты",
"hints.threads.replies_may_be_missing": "Некоторые ответы с других серверов могут отсутствовать.",
"hints.threads.replies_may_be_missing": "Некоторые ответы с других серверов могут здесь отсутствовать.",
"hints.threads.see_more": "Перейдите на {domain}, чтобы увидеть все ответы",
"home.column_settings.show_quotes": "Показывать цитирования",
"home.column_settings.show_reblogs": "Показывать продвижения",
@ -466,7 +466,7 @@
"intervals.full.hours": "{number, plural, one {# час} few {# часа} other {# часов}}",
"intervals.full.minutes": "{number, plural, one {# минута} few {# минуты} other {# минут}}",
"keyboard_shortcuts.back": "перейти назад",
"keyboard_shortcuts.blocked": "открыть список заблокированных",
"keyboard_shortcuts.blocked": "открыть список заблокированных пользователей",
"keyboard_shortcuts.boost": "продвинуть пост",
"keyboard_shortcuts.column": "фокус на одном из столбцов",
"keyboard_shortcuts.compose": "фокус на поле ввода",
@ -483,7 +483,7 @@
"keyboard_shortcuts.legend": "показать эту справку",
"keyboard_shortcuts.local": "перейти к локальной ленте",
"keyboard_shortcuts.mention": "упомянуть автора поста",
"keyboard_shortcuts.muted": "открыть список игнорируемых",
"keyboard_shortcuts.muted": "открыть список игнорируемых пользователей",
"keyboard_shortcuts.my_profile": "перейти к своему профилю",
"keyboard_shortcuts.notifications": "перейти к уведомлениям",
"keyboard_shortcuts.open_media": "открыть медиа",
@ -546,7 +546,7 @@
"mute_modal.they_can_mention_and_follow": "Он сможет упоминать вас и подписаться на вас, но вы этого не увидите.",
"mute_modal.they_wont_know": "Он не будет знать, что вы его игнорируете.",
"mute_modal.title": "Игнорировать пользователя?",
"mute_modal.you_wont_see_mentions": "Вы не увидите посты, которые его упоминают.",
"mute_modal.you_wont_see_mentions": "Вы не будете видеть посты, упоминающие его.",
"mute_modal.you_wont_see_posts": "Он по-прежнему сможет видеть ваши посты, но вы не будете видеть его посты.",
"navigation_bar.about": "О проекте",
"navigation_bar.account_settings": "Пароль и безопасность",
@ -607,9 +607,9 @@
"notification.moderation_warning": "Модераторы вынесли вам предупреждение",
"notification.moderation_warning.action_delete_statuses": "Некоторые ваши посты были удалены.",
"notification.moderation_warning.action_disable": "Ваша учётная запись была отключена.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Некоторые ваши посты были отмечены как контент деликатного характера.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Некоторые ваши посты были отмечены как содержимое деликатного характера.",
"notification.moderation_warning.action_none": "Модераторы вынесли вам предупреждение.",
"notification.moderation_warning.action_sensitive": "С этого момента все ваши новые посты будут отмечены как контент деликатного характера.",
"notification.moderation_warning.action_sensitive": "С этого момента все ваши новые посты будут отмечены как содержимое деликатного характера.",
"notification.moderation_warning.action_silence": "Ваша учётная запись была ограничена.",
"notification.moderation_warning.action_suspend": "Ваша учётная запись была заблокирована.",
"notification.own_poll": "Ваш опрос завершился",
@ -618,9 +618,9 @@
"notification.reblog.name_and_others_with_link": "{name} и ещё <a>{count, plural, one {# пользователь} few {# пользователя} other {# пользователей}}</a> продвинули ваш пост",
"notification.relationships_severance_event": "Разорвана связь с {name}",
"notification.relationships_severance_event.account_suspension": "Администратор сервера {from} заблокировал сервер {target}, поэтому вы больше не сможете получать обновления от людей с этого сервера или взаимодействовать с ними.",
"notification.relationships_severance_event.domain_block": "Администратор сервера {from} заблокировал сервер {target}, где размещены учётные записи у {followersCount} ваших подписчиков и {followingCount, plural, one {# пользователя, на которого вы подписаны} other {# пользователей, на которых вы подписаны}}.",
"notification.relationships_severance_event.domain_block": "Администратор сервера {from} заблокировал сервер {target}, где размещены учётные записи {followersCount} ваших подписчиков и {followingCount, plural, one {# пользователя, на которого вы подписаны} other {# пользователей, на которых вы подписаны}}.",
"notification.relationships_severance_event.learn_more": "Узнать больше",
"notification.relationships_severance_event.user_domain_block": "Вы заблокировали сервер {target}, где размещены учётные записи у {followersCount} ваших подписчиков и {followingCount, plural, one {# пользователя, на которого вы подписаны} other {# пользователей, на которых вы подписаны}}.",
"notification.relationships_severance_event.user_domain_block": "Вы заблокировали сервер {target}, где размещены учётные записи {followersCount} ваших подписчиков и {followingCount, plural, one {# пользователя, на которого вы подписаны} other {# пользователей, на которых вы подписаны}}.",
"notification.status": "{name} опубликовал(а) новый пост",
"notification.update": "{name} отредактировал(а) пост",
"notification_requests.accept": "Принять",

View File

@ -3874,16 +3874,18 @@ a.account__display-name {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 12px;
font-size: 16px;
font-weight: 400;
padding: 12px;
text-decoration: none;
overflow: hidden;
white-space: nowrap;
border: 0;
background: transparent;
color: $secondary-text-color;
background: transparent;
border: 0;
border-left: 4px solid transparent;
box-sizing: border-box;
&:hover,
&:focus,

View File

@ -71,7 +71,7 @@ class Account < ApplicationRecord
INSTANCE_ACTOR_ID = -99
USERNAME_RE = /[a-z0-9_]+([.-]+[a-z0-9_]+)*/i
MENTION_RE = %r{(?<![=/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]]+([.-]+[[:word:]]+)*)?)}
MENTION_RE = %r{(?<![=/[:word:]])@((#{USERNAME_RE})(?:@#{Twitter::TwitterText::Regex[:valid_domain]})?)}
URL_PREFIX_RE = %r{\Ahttp(s?)://[^/]+}
USERNAME_ONLY_RE = /\A#{USERNAME_RE}\z/i
USERNAME_LENGTH_LIMIT = 30

View File

@ -3,10 +3,12 @@
module Account::AttributionDomains
extend ActiveSupport::Concern
ATTRIBUTION_DOMAINS_LIMIT = 100
included do
normalizes :attribution_domains, with: ->(arr) { arr.filter_map { |str| str.to_s.strip.delete_prefix('http://').delete_prefix('https://').delete_prefix('*.').presence }.uniq }
validates :attribution_domains, domain: true, length: { maximum: 100 }, if: -> { local? && will_save_change_to_attribution_domains? }
validates :attribution_domains, domain: true, length: { maximum: ATTRIBUTION_DOMAINS_LIMIT }, if: -> { local? && will_save_change_to_attribution_domains? }
end
def can_be_attributed_from?(domain)

View File

@ -31,7 +31,7 @@ class Quote < ApplicationRecord
belongs_to :quoted_account, class_name: 'Account', optional: true
before_validation :set_accounts
before_validation :set_activity_uri, only: :create, if: -> { account.local? && quoted_account&.remote? }
validates :activity_uri, presence: true, if: -> { account.local? && quoted_account&.remote? }
validate :validate_visibility
@ -69,4 +69,8 @@ class Quote < ApplicationRecord
errors.add(:quoted_status_id, :visibility_mismatch)
end
def set_activity_uri
self.activity_uri = [ActivityPub::TagManager.instance.uri_for(account), '/quote_requests/', SecureRandom.uuid].join
end
end

View File

@ -7,7 +7,7 @@ class ActivityPub::QuoteRequestSerializer < ActivityPub::Serializer
attribute :virtual_object, key: :object
def id
object.activity_uri || [ActivityPub::TagManager.instance.uri_for(object.target_account), '#quote_requests/', object.id].join
object.activity_uri
end
def type

View File

@ -66,6 +66,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
update_interaction_policies!
update_poll!(allow_significant_changes: false)
queue_poll_notifications!
update_quote_approval!
update_counts!
end
end
@ -270,6 +271,23 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
end
end
# This method is only concerned with approval and skips other meaningful changes,
# as it is used instead of `update_quote!` in implicit updates
def update_quote_approval!
quote_uri = @status_parser.quote_uri
return unless quote_uri.present? && @status.quote.present?
quote = @status.quote
return if quote.quoted_status.present? && ActivityPub::TagManager.instance.uri_for(quote.quoted_status) != quote_uri
approval_uri = @status_parser.quote_approval_uri
approval_uri = nil if unsupported_uri_scheme?(approval_uri)
quote.update(approval_uri: approval_uri, state: :pending, legacy: @status_parser.legacy_quote?) if quote.approval_uri != @status_parser.quote_approval_uri
fetch_and_verify_quote!(quote, quote_uri)
end
def update_quote!
quote_uri = @status_parser.quote_uri

View File

@ -56,7 +56,7 @@
.fields-group
= f.input :date_of_birth,
as: :date_of_birth,
hint: t('simple_form.hints.user.date_of_birth', count: Setting.min_age.to_i),
hint: t('simple_form.hints.user.date_of_birth', count: Setting.min_age.to_i, domain: site_hostname),
required: true,
wrapper: :with_block_label

View File

@ -1537,7 +1537,7 @@ da:
domain_blocking: Domæneblokeringsliste
following: Liste over fulgte
lists: Lister
muting: Tavsgørelsesliste
muting: Liste over skjulte
upload: Upload
invites:
delete: Deaktivér

View File

@ -4,7 +4,7 @@ ru:
about_mastodon_html: 'Социальная сеть будущего: никакой рекламы или слежки со стороны корпораций, этичный дизайн и децентрализация! С Mastodon ваши данные находятся только под вашим контролем!'
contact_missing: Не указано
contact_unavailable: N/A
hosted_on: Сервер Mastodon на домене %{domain}
hosted_on: Сервер Mastodon на сайте %{domain}
title: О проекте
accounts:
followers:

View File

@ -149,13 +149,6 @@ ar:
min_age: لا يجوز أن يكون دون السن الأدنى الذي تقتضيه قوانين الدولة.
user:
chosen_languages: إن تم اختيارها، فلن تظهر على الخيوط العامة إلّا الرسائل المنشورة في تلك اللغات
date_of_birth:
few: عليك أن تكون أكبر من %{count} سنة لاستخدام ماستادون. لن نحفظ هذه المعلومة.
many: عليك أن تكون أكبر من %{count} سنة لاستخدام ماستادون. لن نحفظ هذه المعلومة.
one: عليك أن تكون أكبر من %{count} سنة لاستخدام ماستادون. لن نحفظ هذه المعلومة.
other: عليك أن تكون أكبر من %{count} سنة لاستخدام ماستادون. لن نحفظ هذه المعلومة.
two: عليك أن تكون أكبر من %{count} سنة لاستخدام ماستادون. لن نحفظ هذه المعلومة.
zero: عليك أن تكون أكبر من %{count} سنة لاستخدام ماستادون. لن نحفظ هذه المعلومة.
role: الدور يتحكم في أذونات المستخدم.
user_role:
color: اللون الذي سيتم استخدامه للوظيفه في جميع وحدات واجهة المستخدم، كـ RGB بتنسيق hex

View File

@ -148,9 +148,6 @@ bg:
min_age: Не трябва да е под изискваната минимална възраст от закона на юрисдикцията ви.
user:
chosen_languages: Само публикации на отметнатите езици ще се показват в публичните часови оси
date_of_birth:
one: Трябва да се уверим, че сте поне на %{count}, за да употребявате Mastodon. Няма да съхраняваме това.
other: Трябва да се уверим, че сте поне на %{count}, за да употребявате Mastodon. Няма да съхраняваме това.
role: Ролята управлява какви позволения има потребителят.
user_role:
color: Цветът, използван за ролите в потребителския интерфейс, като RGB в шестнадесетичен формат

View File

@ -149,9 +149,6 @@ ca:
min_age: No hauria de ser inferior a l'edat mínima exigida per la llei de la vostra jurisdicció.
user:
chosen_languages: Quan estigui marcat, només es mostraran els tuts de les llengües seleccionades en les línies de temps públiques
date_of_birth:
one: Ens hem d'assegurar que teniu com a mínim %{count} any per a fer servir Mastodon. No ho desarem.
other: Ens hem d'assegurar que teniu com a mínim %{count} anys per a fer servir Mastodon. No ho desarem.
role: El rol controla quins permisos té l'usuari.
user_role:
color: Color que s'usarà per al rol a tota la interfície d'usuari, com a RGB en format hexadecimal

View File

@ -150,11 +150,6 @@ cs:
min_age: Neměla by být pod minimálním věkem požadovaným zákony vaší jurisdikce.
user:
chosen_languages: Po zaškrtnutí budou ve veřejných časových osách zobrazeny pouze příspěvky ve zvolených jazycích
date_of_birth:
few: Musíme se ujistit, že je Vám alespoň %{count}, abyste mohli používat Mastodon. Nebudeme to ukládat.
many: Musíme se ujistit, že je Vám alespoň %{count} let, abyste mohli používat Mastodon. Nebudeme to ukládat.
one: Musíme se ujistit, že je Vám alespoň %{count} rok, abyste mohli používat Mastodon. Nebudeme to ukládat.
other: Musíme se ujistit, že je Vám alespoň %{count} let, abyste mohli používat Mastodon. Nebudeme to ukládat.
role: Role určuje, která oprávnění uživatel má.
user_role:
color: Barva, která má být použita pro roli v celém UI, jako RGB v hex formátu

View File

@ -149,13 +149,6 @@ cy:
min_age: Ni ddylai fod yn is na'r isafswm oedran sy'n ofynnol gan gyfreithiau eich awdurdodaeth.
user:
chosen_languages: Wedi eu dewis, dim ond tŵtiau yn yr ieithoedd hyn bydd yn cael eu harddangos mewn ffrydiau cyhoeddus
date_of_birth:
few: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn.
many: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn.
one: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn.
other: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn.
two: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn.
zero: Gwnewch yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn.
role: Mae'r rôl yn rheoli pa ganiatâd sydd gan y defnyddiwr.
user_role:
color: Lliw i'w ddefnyddio ar gyfer y rôl drwy'r UI, fel RGB mewn fformat hecs

View File

@ -150,9 +150,6 @@ da:
min_age: Bør ikke være under den iht. lovgivningen i det aktuelle retsområde krævede minimumsalder.
user:
chosen_languages: Når markeret, vil kun indlæg på de valgte sprog fremgå på offentlige tidslinjer
date_of_birth:
one: Vi er nødt til at sikre, at man er fyldt %{count} for at bruge Mastodon. Denne information gemmes ikke.
other: Vi er nødt til at sikre, at man er fyldt %{count} for at bruge Mastodon. Denne information gemmes ikke.
role: Rollen styrer, hvilke tilladelser brugeren er tildelt.
user_role:
color: Farven, i RGB hex-format, der skal bruges til rollen i hele UI'en

View File

@ -150,9 +150,6 @@ de:
min_age: Sollte nicht unter dem gesetzlich vorgeschriebenen Mindestalter liegen.
user:
chosen_languages: Wenn du hier eine oder mehrere Sprachen auswählst, werden ausschließlich Beiträge in diesen Sprachen in deinen öffentlichen Timelines angezeigt
date_of_birth:
one: Wir müssen sicherstellen, dass du mindestens %{count} Jahre alt bist, um Mastodon nutzen zu können. Wir werden diese Information nicht aufbewahren.
other: Wir müssen sicherstellen, dass du mindestens %{count} Jahre alt bist, um Mastodon nutzen zu können. Wir werden diese Information nicht aufbewahren.
role: Die Rolle bestimmt, welche Berechtigungen das Konto hat.
user_role:
color: Farbe, die für diese Rolle in der gesamten Benutzerschnittstelle verwendet wird, als RGB im Hexadezimalsystem

View File

@ -150,9 +150,6 @@ el:
min_age: Δεν πρέπει να είναι κάτω από την ελάχιστη ηλικία που απαιτείται από τους νόμους της δικαιοδοσίας σας.
user:
chosen_languages: Όταν ενεργοποιηθεί, στη δημόσια ροή θα εμφανίζονται τουτ μόνο από τις επιλεγμένες γλώσσες
date_of_birth:
one: Πρέπει να βεβαιωθούμε ότι είσαι τουλάχιστον %{count} για να χρησιμοποιήσεις το Mastodon. Δε θα το αποθηκεύσουμε.
other: Πρέπει να βεβαιωθούμε ότι είσαι τουλάχιστον %{count} για να χρησιμοποιήσεις το Mastodon. Δε θα το αποθηκεύσουμε.
role: Ο ρόλος ελέγχει ποια δικαιώματα έχει ο χρήστης.
user_role:
color: Το χρώμα που θα χρησιμοποιηθεί για το ρόλο σε ολόκληρη τη διεπαφή, ως RGB σε δεκαεξαδική μορφή

View File

@ -151,8 +151,8 @@ en-GB:
user:
chosen_languages: When checked, only posts in selected languages will be displayed in public timelines
date_of_birth:
one: We have to make sure you're at least %{count} to use Mastodon. We won't store this.
other: We have to make sure you're at least %{count} to use Mastodon. We won't store this.
one: We have to make sure you're at least %{count} to use %{domain}. We won't store this.
other: We have to make sure you're at least %{count} to use %{domain}. We won't store this.
role: The role controls which permissions the user has.
user_role:
color: Color to be used for the role throughout the UI, as RGB in hex format

View File

@ -151,8 +151,8 @@ en:
user:
chosen_languages: When checked, only posts in selected languages will be displayed in public timelines
date_of_birth:
one: We have to make sure you're at least %{count} to use Mastodon. We won't store this.
other: We have to make sure you're at least %{count} to use Mastodon. We won't store this.
one: We have to make sure you're at least %{count} to use %{domain}. We won't store this.
other: We have to make sure you're at least %{count} to use %{domain}. We won't store this.
role: The role controls which permissions the user has.
user_role:
color: Color to be used for the role throughout the UI, as RGB in hex format

View File

@ -150,9 +150,6 @@ es-AR:
min_age: No debería estar por debajo de la edad mínima requerida por las leyes de su jurisdicción.
user:
chosen_languages: Cuando estén marcados, sólo se mostrarán los mensajes en los idiomas seleccionados en las líneas temporales públicas
date_of_birth:
one: Tenemos que asegurarnos de que al menos tenés %{count} años de edad para usar Mastodon. No almacenaremos esta información.
other: Tenemos que asegurarnos de que al menos tenés %{count} años de edad para usar Mastodon. No almacenaremos esta información.
role: El rol controla qué permisos tiene el usuario.
user_role:
color: Color que se utilizará para el rol a lo largo de la interface de usuario, como RGB en formato hexadecimal

View File

@ -150,9 +150,6 @@ es-MX:
min_age: No debe ser menor de la edad mínima exigida por las leyes de su jurisdicción.
user:
chosen_languages: Cuando se marca, solo se mostrarán las publicaciones en los idiomas seleccionados en las líneas de tiempo públicas
date_of_birth:
one: Necesitamos asegurarnos de que tengas al menos %{count} para usar Mastodon. No guardaremos esta información.
other: Necesitamos asegurarnos de que tengas al menos %{count} para usar Mastodon. No guardaremos esta información.
role: El rol controla qué permisos tiene el usuario.
user_role:
color: Color que se usará para el rol en toda la interfaz de usuario, como RGB en formato hexadecimal

View File

@ -150,9 +150,6 @@ es:
min_age: No debería estar por debajo de la edad mínima requerida por las leyes de su jurisdicción.
user:
chosen_languages: Cuando se marca, solo se mostrarán las publicaciones en los idiomas seleccionados en las líneas de tiempo públicas
date_of_birth:
one: Tenemos que asegurarnos de que tienes al menos %{count} años para usar Mastodon. No guardaremos este dato.
other: Tenemos que asegurarnos de que tienes al menos %{count} años para usar Mastodon. No guardaremos este dato.
role: El rol controla qué permisos tiene el usuario.
user_role:
color: Color que se utilizará para el rol a lo largo de la interfaz de usuario, como RGB en formato hexadecimal

View File

@ -149,9 +149,6 @@ fa:
min_age: نباید کم‌تر از کمینهٔ زمان لازم از سوی قوانین حقوقیتان باشد.
user:
chosen_languages: اگر انتخاب کنید، تنها نوشته‌هایی که به زبان‌های برگزیدهٔ شما نوشته شده‌اند در فهرست نوشته‌های عمومی نشان داده می‌شوند
date_of_birth:
one: برای استفاده از ماستودون باید مطمئن شویم کمینه %{count} سال را دارید. این مورد را ذخیره نخواهیم کرد.
other: برای استفاده از ماستودون باید مطمئن شویم کمینه %{count} سال را دارید. این مورد را ذخیره نخواهیم کرد.
role: نقش کنترل می کند که کاربر چه مجوزهایی دارد.
user_role:
color: رنگی که برای نقش در سرتاسر UI استفاده می شود، به عنوان RGB در قالب هگز

View File

@ -148,9 +148,6 @@ fi:
min_age: Ei pidä alittaa lainkäyttöalueesi lakien vaatimaa vähimmäisikää.
user:
chosen_languages: Jos valitset kieliä oheisesta luettelosta, vain niidenkieliset julkaisut näkyvät sinulle julkisilla aikajanoilla
date_of_birth:
one: Meidän on varmistettava, että olet vähintään %{count}, jotta voi käyttää Mastodonia. Emme tallenna tätä.
other: Meidän on varmistettava, että olet vähintään %{count}, jotta voi käyttää Mastodonia. Emme tallenna tätä.
role: Rooli määrää, millaiset käyttöoikeudet käyttäjällä on.
user_role:
color: Väri, jota käytetään roolille kaikkialla käyttöliittymässä, RGB-heksadesimaalimuodossa

View File

@ -150,9 +150,6 @@ fo:
min_age: Eigur ikki at vera undir lægsta aldri, sum lógirnar í tínum rættarøki krevja.
user:
chosen_languages: Tá hetta er valt, verða einans postar í valdum málum vístir á almennum tíðarlinjum
date_of_birth:
one: Vit mugu tryggja okkum, at tú er í minsta lagi %{count} fyri at brúka Mastodon. Vit goyma ikki hesar upplýsingar.
other: Vit mugu tryggja okkum, at tú er í minsta lagi %{count} fyri at brúka Mastodon. Vit goyma ikki hesar upplýsingar.
role: Leikluturin stýrir hvørji rættindi, brúkarin hevur.
user_role:
color: Litur, sum leikluturin hevur í øllum brúkaramarkamótinum, sum RGB og upplýst sum sekstandatal

View File

@ -150,9 +150,6 @@ fy:
min_age: Mei net leger wêze as de minimale fereaske leeftiid neffens de wetten fan jo jurisdiksje.
user:
chosen_languages: Allinnich berjochten yn de selektearre talen wurde op de iepenbiere tiidline toand
date_of_birth:
one: Wy moatte derfoar soargje dat jo op syn minst %{count} binne om Mastodon te brûken. Dit wurdt net bewarre.
other: Wy moatte derfoar soargje dat jo op syn minst %{count} binne om Mastodon te brûken. Dit wurdt net bewarre.
role: De rol bepaalt hokker rjochten in brûker hat.
user_role:
color: Kleur dyt brûkt wurdt foar de rol yn de UI, as RGB yn heksadesimaal formaat

View File

@ -150,12 +150,6 @@ ga:
min_age: Níor chóir go mbeidís faoi bhun na haoise íosta a éilíonn dlíthe do dhlínse.
user:
chosen_languages: Nuair a dhéantar iad a sheiceáil, ní thaispeánfar ach postálacha i dteangacha roghnaithe in amlínte poiblí
date_of_birth:
few: Caithfimid a chinntiú go bhfuil tú %{count} ar a laghad chun Mastodon a úsáid. Ní stórálfaimid é seo.
many: Caithfimid a chinntiú go bhfuil tú %{count} ar a laghad chun Mastodon a úsáid. Ní stórálfaimid é seo.
one: Caithfimid a chinntiú go bhfuil tú %{count} ar a laghad chun Mastodon a úsáid. Ní stórálfaimid é seo.
other: Caithfimid a chinntiú go bhfuil tú %{count} ar a laghad chun Mastodon a úsáid. Ní stórálfaimid é seo.
two: Caithfimid a chinntiú go bhfuil tú %{count} ar a laghad chun Mastodon a úsáid. Ní stórálfaimid é seo.
role: Rialaíonn an ról na ceadanna atá ag an úsáideoir.
user_role:
color: Dath le húsáid don ról ar fud an Chomhéadain, mar RGB i bhformáid heicsidheachúlach

View File

@ -149,11 +149,6 @@ gd:
min_age: Cha bu chòir seo a bhith fon aois as lugha a dhiarras laghain an t-uachdranais laghail agad.
user:
chosen_languages: Nuair a bhios cromag ris, cha nochd ach postaichean sna cànain a thagh thu air loidhnichean-ama poblach
date_of_birth:
few: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bliadhnaichean a dhaois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
one: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bhliadhna a dhaois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
other: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bliadhna a dhaois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
two: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bhliadhna a dhaois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
role: Stiùiridh an dreuchd dè na ceadan a bhios aig cleachdaiche.
user_role:
color: An datha a bhios air an dreuchd air feadh na h-eadar-aghaidh, na RGB san fhòrmat sia-dheicheach

View File

@ -150,9 +150,6 @@ gl:
min_age: Non debería ser inferior á idade mínima requerida polas leis da túa xurisdición.
user:
chosen_languages: Se ten marca, só as publicacións nos idiomas seleccionados serán mostrados en cronoloxías públicas
date_of_birth:
one: Temos que confirmar que tes %{count} anos polo menos para usar Mastodon. Non gardamos este dato.
other: Temos que confirmar que tes %{count} anos polo menos para usar Mastodon. Non gardamos este dato.
role: Os roles establecen os permisos que ten a usuaria.
user_role:
color: Cor que se usará para o rol a través da IU, como RGB en formato hex

View File

@ -150,11 +150,6 @@ he:
min_age: על הערך להיות לפחות בגיל המינימלי הדרוש בחוק באיזור השיפוט שלך.
user:
chosen_languages: אם פעיל, רק הודעות בשפות הנבחרות יוצגו לפידים הפומביים
date_of_birth:
many: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו.
one: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו.
other: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו.
two: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו.
role: התפקיד שולט על אילו הרשאות יש למשתמש.
user_role:
color: צבע לתפקיד בממשק המשתמש, כ RGB בפורמט הקסדצימלי

View File

@ -150,9 +150,6 @@ hu:
min_age: Nem lehet a joghatóság által meghatározott minimális kor alatt.
user:
chosen_languages: Ha aktív, csak a kiválasztott nyelvű bejegyzések jelennek majd meg a nyilvános idővonalon
date_of_birth:
one: Ahhoz, hogy a Mastodont használd, meg kell győződnünk arról, hogy legalább %{count} éves vagy. Ezt nem tároljuk.
other: Ahhoz, hogy a Mastodont használd, meg kell győződnünk arról, hogy legalább %{count} éves vagy. Ezt nem tároljuk.
role: A szerep szabályozza, hogy a felhasználó milyen jogosultságokkal rendelkezik.
user_role:
color: A szerephez használandó szín mindenhol a felhasználói felületen, hexa RGB formátumban

View File

@ -145,9 +145,6 @@ ia:
min_age: Non deberea esser infra le etate minime requirite per le leges de tu jurisdiction.
user:
chosen_languages: Si marcate, solmente le messages in le linguas seligite apparera in chronologias public
date_of_birth:
one: Nos debe assecurar que tu ha al minus %{count} anno pro usar Mastodon. Nos non va immagazinar isto.
other: Nos debe assecurar que tu ha al minus %{count} annos pro usar Mastodon. Nos non va immagazinar isto.
role: Le rolo controla qual permissos le usator ha.
user_role:
color: Color a esser usate pro le rolo in omne parte del UI, como RGB in formato hexadecimal

View File

@ -150,9 +150,6 @@ is:
min_age: Ætti ekki að vera lægri en sá lágmarksaldur sek kveðið er á um í lögum þíns lögsagnarumdæmis.
user:
chosen_languages: Þegar merkt er við þetta, birtast einungis færslur á völdum tungumálum á opinberum tímalínum
date_of_birth:
one: Við verðum að ganga úr skugga um að þú hafir náð %{count} aldri til að nota Mastodon. Við munum ekki geyma þessar upplýsingar.
other: Við verðum að ganga úr skugga um að þú hafir náð %{count} aldri til að nota Mastodon. Við munum ekki geyma þessar upplýsingar.
role: Hlutverk stýrir hvaða heimildir notandinn hefur.
user_role:
color: Litur sem notaður er fyrir hlutverkið allsstaðar í viðmótinu, sem RGB-gildi á hex-sniði

View File

@ -150,9 +150,6 @@ it:
min_age: Non si dovrebbe avere un'età inferiore a quella minima richiesta, dalle leggi della tua giurisdizione.
user:
chosen_languages: Quando una o più lingue sono contrassegnate, nelle timeline pubbliche vengono mostrati solo i toot nelle lingue selezionate
date_of_birth:
one: Dobbiamo verificare che tu abbia almeno %{count} anno per usare Mastodon. Non archivieremo questa informazione.
other: Dobbiamo verificare che tu abbia almeno %{count} anni per usare Mastodon. Non archivieremo questa informazione.
role: Il ruolo controlla quali permessi ha l'utente.
user_role:
color: Colore da usare per il ruolo in tutta l'UI, come RGB in formato esadecimale

View File

@ -149,8 +149,6 @@ ja:
min_age: お住まいの国や地域の法律によって定められている最低年齢を下回ってはなりません。
user:
chosen_languages: 選択すると、選択した言語の投稿のみが公開タイムラインに表示されるようになります
date_of_birth:
other: Mastodonを利用するには少なくとも%{count}歳以上であることを確認する必要があります。この情報は保存されません。
role: そのロールは、ユーザーが持つ権限を制御します。
user_role:
color: UI 全体でロールの表示に使用される色16進数RGB形式

View File

@ -150,8 +150,6 @@ ko:
min_age: 관할지역의 법률에서 요구하는 최저 연령보다 작으면 안 됩니다.
user:
chosen_languages: 체크하면, 선택 된 언어로 작성된 게시물들만 공개 타임라인에 보여집니다
date_of_birth:
other: 마스토돈을 사용하려면 %{count}세 이상임을 확인해야 합니다. 이 정보는 저장되지 않습니다.
role: 역할은 사용자가 어떤 권한을 가지게 될 지 결정합니다.
user_role:
color: 색상은 사용자 인터페이스에서 역할을 나타내기 위해 사용되며, RGB 16진수 형식입니다

View File

@ -143,10 +143,6 @@ lv:
domain: Sniegtā tiešsaistas pakalpojuma neatkārtojama identifikācija.
user:
chosen_languages: Ja ieķeksēts, publiskos laika grafikos tiks parādītas tikai ziņas noteiktajās valodās
date_of_birth:
one: Mums jāpārliecinās, ka jums ir vismaz %{count} gads, lai varētu izmantot Mastodonu. Mēs neuzglabāsim šo informāciju.
other: Mums jāpārliecinās, ka jums ir vismaz %{count} gadi, lai varētu izmantot Mastodonu. Mēs neuzglabāsim šo informāciju.
zero: Mums jāpārliecinās, ka jums ir vismaz %{count} gadi, lai varētu izmantot Mastodonu. Mēs neuzglabāsim šo informāciju.
role: Loma nosaka, kādas lietotājam ir atļaujas.
user_role:
color: Krāsa, kas jāizmanto lomai visā lietotāja saskarnē, kā RGB hex formātā

View File

@ -150,9 +150,6 @@ nl:
min_age: Mag niet lager zijn dan de minimale vereiste leeftijd volgens de wetten van jouw jurisdictie.
user:
chosen_languages: Alleen berichten in de aangevinkte talen worden op de openbare tijdlijnen getoond
date_of_birth:
one: We moeten er zeker van zijn dat je tenminste %{count} bent om Mastodon te mogen gebruiken. Deze informatie wordt niet door ons opgeslagen.
other: We moeten er zeker van zijn dat je tenminste %{count} bent om Mastodon te mogen gebruiken. Deze informatie wordt niet door ons opgeslagen.
role: De rol bepaalt welke rechten de gebruiker heeft.
user_role:
color: Kleur die gebruikt wordt voor de rol in de UI, als RGB in hexadecimale formaat

View File

@ -149,9 +149,6 @@ nn:
min_age: Skal ikkje vere under minstealder som krevst av lover i jurisdiksjonen din.
user:
chosen_languages: Når merka vil berre tuta på dei valde språka synast på offentlege tidsliner
date_of_birth:
one: Me må syta for at du er minst %{count} for å bruka Masodon. Me lagrar ikkje dette.
other: Me må syta for at du er minst %{count} for å bruka Masodon. Me lagrar ikkje dette.
role: Rolla kontrollerer kva løyve brukaren har.
user_role:
color: Fargen som skal nyttast for denne rolla i heile brukargrensesnittet, som RGB i hex-format

View File

@ -149,9 +149,6 @@ pt-BR:
min_age: Não deve ter menos que a idade mínima exigida pelas suas leis locais.
user:
chosen_languages: Apenas as publicações dos idiomas selecionados serão exibidas nas linhas públicas
date_of_birth:
one: Temos que ter certeza de que você é pelo menos %{count} para usar o Mastodon. Não vamos armazenar isso.
other: Precisamos ter certeza de que você tem, no mínimo, %{count} anos para usar o Mastodon. Não armazenaremos essa informação.
role: A função controla quais permissões o usuário tem.
user_role:
color: Cor a ser usada para o cargo em toda a interface do usuário, como RGB no formato hexadecimal

View File

@ -149,9 +149,6 @@ pt-PT:
min_age: Não deve ter menos do que a idade mínima exigida pela legislação da sua jurisdição.
user:
chosen_languages: Quando selecionado, só serão mostradas nas cronologias públicas as publicações nos idiomas escolhidos
date_of_birth:
one: Temos de nos certificar que tem pelo menos %{count} para utilizar o Mastodon. Não vamos guardar esta informação.
other: Temos de nos certificar que tem pelo menos %{count} para utilizar o Mastodon. Não vamos guardar esta informação.
role: A função controla as permissões que o utilizador tem.
user_role:
color: Cor a ser utilizada para a função em toda a interface de utilizador, como RGB no formato hexadecimal

View File

@ -149,11 +149,6 @@ ru:
min_age: Не меньше минимального возраста, требуемого по закону в вашей юрисдикции.
user:
chosen_languages: Отметьте языки, на которых вы желаете видеть посты в публичных лентах. Оставьте выбор пустым, чтобы не фильтровать посты по языку
date_of_birth:
few: Нужно убедиться, что вам не меньше %{count} лет. Мы не храним введённые здесь данные.
many: Нужно убедиться, что вам не меньше %{count} лет. Мы не храним введённые здесь данные.
one: Нужно убедиться, что вам не меньше %{count} года. Мы не храним введённые здесь данные.
other: Нужно убедиться, что вам не меньше %{count} лет. Мы не храним введённые здесь данные.
role: Роль определяет, какими правами обладает пользователь.
user_role:
color: Цвет, который будет использоваться для роли в интерфейсе (UI), как RGB в формате HEX

View File

@ -149,9 +149,6 @@ si:
min_age: ඔබගේ අධිකරණ බල ප්‍රදේශයේ නීති මගින් අවශ්‍ය අවම වයසට වඩා අඩු නොවිය යුතුය.
user:
chosen_languages: සබල නම්, තෝරාගත් භාෂාවල ලිපි පමණක් ප්‍රසිද්ධ කාල රේඛාවේ දිස්වේ
date_of_birth:
one: Mastodon භාවිතා කිරීමට ඔබ අවම වශයෙන් %{count} ක් වත් ඇති බවට අපි සහතික විය යුතුයි. අපි මෙය ගබඩා නොකරමු.
other: Mastodon භාවිතා කිරීමට ඔබ අවම වශයෙන් %{count} ක් වත් ඇති බවට අපි සහතික විය යුතුයි. අපි මෙය ගබඩා නොකරමු.
role: පරිශීලකයාට ඇති අවසරයන් භූමිකාව පාලනය කරයි.
user_role:
color: UI පුරා භූමිකාව සඳහා භාවිතා කළ යුතු වර්ණය, හෙක්ස් ආකෘතියෙන් RGB ලෙස

View File

@ -150,9 +150,6 @@ sv:
min_age: Bör inte vara lägre än den minimiålder som krävs enligt lagarna i din jurisdiktion.
user:
chosen_languages: Vid aktivering visas bara inlägg på dina valda språk i offentliga tidslinjer
date_of_birth:
one: Vi måste se till att du är minst %{count} för att använda Mastodon. Vi lagrar inte denna information.
other: Vi måste se till att du är minst %{count} för att använda Mastodon. Vi lagrar inte denna information.
role: Rollen styr vilka behörigheter användaren har.
user_role:
color: Färgen som ska användas för rollen i användargränssnittet, som RGB i hex-format

View File

@ -150,9 +150,6 @@ tr:
min_age: Tabi olduğunuz yasaların gerektirdiği yaştan düşük olmamalıdır.
user:
chosen_languages: İşaretlendiğinde, yalnızca seçilen dillerdeki gönderiler genel zaman çizelgelerinde görüntülenir
date_of_birth:
one: Mastodon kullanmak için en az %{count} yaşında olduğunuzdan emin olmalıyız. Bu bilgiyi saklamıyoruz.
other: Mastodon kullanmak için en az %{count} yaşında olduğunuzdan emin olmalıyız. Bu bilgiyi saklamıyoruz.
role: Rol, kullanıcıların sahip olduğu izinleri denetler.
user_role:
color: Arayüz boyunca rol için kullanılacak olan renk, hex biçiminde RGB

View File

@ -150,8 +150,6 @@ vi:
min_age: Không được dưới độ tuổi tối thiểu theo quy định của luật pháp tại khu vực của bạn.
user:
chosen_languages: Chỉ hiển thị những ngôn ngữ tút sau
date_of_birth:
other: Chúng tôi cần đảm bảo rằng bạn lớn hơn %{count} tuổi để sử dụng Mastodon. Chúng tôi không lưu trữ thông tin này.
role: Vai trò kiểm soát những quyền mà người dùng có.
user_role:
color: Màu được sử dụng cho vai trò trong toàn bộ giao diện người dùng, dưới dạng RGB ở định dạng hex

View File

@ -149,8 +149,6 @@ zh-CN:
min_age: 不应低于您所在地法律管辖权要求的最低年龄。
user:
chosen_languages: 仅选中语言的嘟文会出现在公共时间线上(全不选则显示所有语言的嘟文)
date_of_birth:
other: 我们必须确认%{count}岁以上的用户才能使用Mastodon。我们不会存储该信息。
role: 角色用于控制用户拥有的权限。
user_role:
color: 在界面各处用于标记该角色的颜色,以十六进制 RGB 格式表示

View File

@ -150,8 +150,6 @@ zh-TW:
min_age: 不應低於您所屬法律管轄區要求之最低年齡。
user:
chosen_languages: 當選取時,只有選取語言之嘟文會於公開時間軸中顯示
date_of_birth:
other: 我們必須確認您至少年滿 %{count} 以使用 Mastodon。我們不會儲存此資料。
role: 角色控制使用者有哪些權限。
user_role:
color: 於整個使用者介面中用於角色的顏色,十六進位格式的 RGB

View File

@ -6,66 +6,47 @@ RSpec.describe ActivityPub::Activity::Accept do
let(:sender) { Fabricate(:account) }
let(:recipient) { Fabricate(:account) }
let(:json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Accept',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: {
id: 'bar',
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(recipient),
object: ActivityPub::TagManager.instance.uri_for(sender),
},
}.with_indifferent_access
end
describe '#perform' do
subject { described_class.new(json, sender) }
before do
allow(RemoteAccountRefreshWorker).to receive(:perform_async)
Fabricate(:follow_request, account: recipient, target_account: sender)
subject.perform
end
context 'with a Follow request' do
let(:json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Accept',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: {
id: 'https://abc-123/456',
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(recipient),
object: ActivityPub::TagManager.instance.uri_for(sender),
},
}.deep_stringify_keys
end
it 'creates a follow relationship' do
expect(recipient.following?(sender)).to be true
end
context 'with a regular Follow' do
before do
Fabricate(:follow_request, account: recipient, target_account: sender)
end
it 'removes the follow request' do
expect(recipient.requested?(sender)).to be false
end
it 'creates a follow relationship, removes the follow request, and queues a refresh' do
expect { subject.perform }
.to change { recipient.following?(sender) }.from(false).to(true)
.and change { recipient.requested?(sender) }.from(true).to(false)
it 'queues a refresh' do
expect(RemoteAccountRefreshWorker).to have_received(:perform_async).with(sender.id)
end
end
expect(RemoteAccountRefreshWorker).to have_enqueued_sidekiq_job(sender.id)
end
end
context 'when given a relay' do
subject { described_class.new(json, sender) }
context 'when given a relay' do
let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') }
let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') }
let(:json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Accept',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: {
id: 'https://abc-123/456',
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(recipient),
object: ActivityPub::TagManager.instance.uri_for(sender),
},
}.with_indifferent_access
end
it 'marks the relay as accepted' do
subject.perform
expect(relay.reload.accepted?).to be true
it 'marks the relay as accepted' do
expect { subject.perform }
.to change { relay.reload.accepted? }.from(false).to(true)
end
end
end
end
end

View File

@ -5,14 +5,6 @@ require 'rails_helper'
RSpec.describe ActivityPub::Activity::Reject do
let(:sender) { Fabricate(:account) }
let(:recipient) { Fabricate(:account) }
let(:object_json) do
{
id: 'bar',
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(recipient),
object: ActivityPub::TagManager.instance.uri_for(sender),
}
end
let(:json) do
{
@ -21,130 +13,117 @@ RSpec.describe ActivityPub::Activity::Reject do
type: 'Reject',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: object_json,
}.with_indifferent_access
}.deep_stringify_keys
end
describe '#perform' do
subject { described_class.new(json, sender) }
context 'when rejecting a pending follow request by target' do
before do
Fabricate(:follow_request, account: recipient, target_account: sender)
subject.perform
context 'when rejecting a Follow' do
let(:object_json) do
{
id: 'bar',
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(recipient),
object: ActivityPub::TagManager.instance.uri_for(sender),
}
end
it 'does not create a follow relationship' do
expect(recipient.following?(sender)).to be false
context 'when rejecting a pending follow request by target' do
before do
Fabricate(:follow_request, account: recipient, target_account: sender)
end
it 'removes the follow request without creating a follow relationship' do
expect { subject.perform }
.to change { recipient.requested?(sender) }.from(true).to(false)
.and not_change { recipient.following?(sender) }.from(false)
end
end
it 'removes the follow request' do
expect(recipient.requested?(sender)).to be false
context 'when rejecting a pending follow request by uri' do
before do
Fabricate(:follow_request, account: recipient, target_account: sender, uri: 'bar')
end
it 'removes the follow request without creating a follow relationship' do
expect { subject.perform }
.to change { recipient.requested?(sender) }.from(true).to(false)
.and not_change { recipient.following?(sender) }.from(false)
end
end
context 'when rejecting a pending follow request by uri only' do
let(:object_json) { 'bar' }
before do
Fabricate(:follow_request, account: recipient, target_account: sender, uri: 'bar')
end
it 'removes the follow request without creating a follow relationship' do
expect { subject.perform }
.to change { recipient.requested?(sender) }.from(true).to(false)
.and not_change { recipient.following?(sender) }.from(false)
end
end
context 'when rejecting an existing follow relationship by target' do
before do
Fabricate(:follow, account: recipient, target_account: sender)
end
it 'removes the follow relationship without creating a request' do
expect { subject.perform }
.to change { recipient.following?(sender) }.from(true).to(false)
.and not_change { recipient.requested?(sender) }.from(false)
end
end
context 'when rejecting an existing follow relationship by uri' do
before do
Fabricate(:follow, account: recipient, target_account: sender, uri: 'bar')
end
it 'removes the follow relationship without creating a request' do
expect { subject.perform }
.to change { recipient.following?(sender) }.from(true).to(false)
.and not_change { recipient.requested?(sender) }.from(false)
end
end
context 'when rejecting an existing follow relationship by uri only' do
let(:object_json) { 'bar' }
before do
Fabricate(:follow, account: recipient, target_account: sender, uri: 'bar')
end
it 'removes the follow relationship without creating a request' do
expect { subject.perform }
.to change { recipient.following?(sender) }.from(true).to(false)
.and not_change { recipient.requested?(sender) }.from(false)
end
end
end
context 'when rejecting a pending follow request by uri' do
before do
Fabricate(:follow_request, account: recipient, target_account: sender, uri: 'bar')
subject.perform
end
context 'when given a relay' do
subject { described_class.new(json, sender) }
it 'does not create a follow relationship' do
expect(recipient.following?(sender)).to be false
end
let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') }
it 'removes the follow request' do
expect(recipient.requested?(sender)).to be false
end
end
context 'when rejecting a pending follow request by uri only' do
let(:object_json) { 'bar' }
before do
Fabricate(:follow_request, account: recipient, target_account: sender, uri: 'bar')
subject.perform
end
it 'does not create a follow relationship' do
expect(recipient.following?(sender)).to be false
end
it 'removes the follow request' do
expect(recipient.requested?(sender)).to be false
end
end
context 'when rejecting an existing follow relationship by target' do
before do
Fabricate(:follow, account: recipient, target_account: sender)
subject.perform
end
it 'removes the follow relationship' do
expect(recipient.following?(sender)).to be false
end
it 'does not create a follow request' do
expect(recipient.requested?(sender)).to be false
end
end
context 'when rejecting an existing follow relationship by uri' do
before do
Fabricate(:follow, account: recipient, target_account: sender, uri: 'bar')
subject.perform
end
it 'removes the follow relationship' do
expect(recipient.following?(sender)).to be false
end
it 'does not create a follow request' do
expect(recipient.requested?(sender)).to be false
end
end
context 'when rejecting an existing follow relationship by uri only' do
let(:object_json) { 'bar' }
before do
Fabricate(:follow, account: recipient, target_account: sender, uri: 'bar')
subject.perform
end
it 'removes the follow relationship' do
expect(recipient.following?(sender)).to be false
end
it 'does not create a follow request' do
expect(recipient.requested?(sender)).to be false
end
end
end
context 'when given a relay' do
subject { described_class.new(json, sender) }
let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') }
let(:json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Reject',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: {
let(:object_json) do
{
id: 'https://abc-123/456',
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(recipient),
object: ActivityPub::TagManager.instance.uri_for(sender),
},
}.with_indifferent_access
end
}.with_indifferent_access
end
it 'marks the relay as rejected' do
subject.perform
expect(relay.reload.rejected?).to be true
it 'marks the relay as rejected' do
subject.perform
expect(relay.reload.rejected?).to be true
end
end
end
end

View File

@ -401,6 +401,10 @@ RSpec.describe Account do
expect(subject.match('@alice@example.com')[1]).to eq 'alice@example.com'
end
it 'matches full usernames with unicode domain names' do
expect(subject.match('@alice@🌈🌈🌈.st')[1]).to eq 'alice@🌈🌈🌈.st'
end
it 'matches full usernames with a dot at the end' do
expect(subject.match('Hello @alice@example.com.')[1]).to eq 'alice@example.com'
end
@ -506,6 +510,8 @@ RSpec.describe Account do
context 'when account is local' do
subject { Fabricate.build :account, domain: nil }
let(:domains_limit) { described_class::ATTRIBUTION_DOMAINS_LIMIT }
context 'with an existing differently-cased username account' do
before { Fabricate :account, username: 'the_doctor' }
@ -547,8 +553,8 @@ RSpec.describe Account do
it { is_expected.to validate_absence_of(:shared_inbox_url).on(:create) }
it { is_expected.to validate_absence_of(:uri).on(:create) }
it { is_expected.to allow_values([], ['example.com'], (1..100).to_a).for(:attribution_domains) }
it { is_expected.to_not allow_values(['example com'], ['@'], (1..101).to_a).for(:attribution_domains) }
it { is_expected.to allow_values([], ['example.com'], (1..domains_limit).to_a).for(:attribution_domains) }
it { is_expected.to_not allow_values(['example com'], ['@'], (1..(domains_limit + 1)).to_a).for(:attribution_domains) }
end
context 'when account is remote' do

View File

@ -435,7 +435,71 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status has an existing unverified quote and adds an approval link' do
context 'when the status has an existing unverified quote and adds an approval link through an implicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: nil) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
let(:payload) do
{
'@context': [
'https://www.w3.org/ns/activitystreams',
{
'@id': 'https://w3id.org/fep/044f#quote',
'@type': '@id',
},
{
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
'@type': '@id',
},
],
id: 'foo',
type: 'Note',
summary: 'Show more',
content: 'Hello universe',
quote: ActivityPub::TagManager.instance.uri_for(quoted_status),
quoteAuthorization: approval_uri,
}
end
before do
stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
'@context': [
'https://www.w3.org/ns/activitystreams',
{
QuoteAuthorization: 'https://w3id.org/fep/044f#QuoteAuthorization',
gts: 'https://gotosocial.org/ns#',
interactionPolicy: {
'@id': 'gts:interactionPolicy',
'@type': '@id',
},
interactingObject: {
'@id': 'gts:interactingObject',
'@type': '@id',
},
interactionTarget: {
'@id': 'gts:interactionTarget',
'@type': '@id',
},
},
],
type: 'QuoteAuthorization',
id: approval_uri,
attributedTo: ActivityPub::TagManager.instance.uri_for(quoted_status.account),
interactingObject: ActivityPub::TagManager.instance.uri_for(status),
interactionTarget: ActivityPub::TagManager.instance.uri_for(quoted_status),
}))
end
it 'updates the approval URI and verifies the quote' do
expect { subject.call(status, json, json) }
.to change(quote, :approval_uri).to(approval_uri)
.and change(quote, :state).to('accepted')
end
end
context 'when the status has an existing unverified quote and adds an approval link through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: nil) }
@ -500,7 +564,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status has an existing verified quote and removes an approval link' do
context 'when the status has an existing verified quote and removes an approval link through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
@ -535,7 +599,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status adds a verifiable quote' do
context 'when the status adds a verifiable quote through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
@ -600,7 +664,39 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status adds a unverifiable quote' do
context 'when the status adds a unverifiable quote through an implicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
let(:payload) do
{
'@context': [
'https://www.w3.org/ns/activitystreams',
{
'@id': 'https://w3id.org/fep/044f#quote',
'@type': '@id',
},
{
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
'@type': '@id',
},
],
id: 'foo',
type: 'Note',
summary: 'Show more',
content: 'Hello universe',
quote: ActivityPub::TagManager.instance.uri_for(quoted_status),
}
end
it 'does not add the quote' do
expect { subject.call(status, json, json) }
.to not_change(status, :quote).from(nil)
end
end
context 'when the status adds a unverifiable quote through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
@ -635,7 +731,29 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status removes a verified quote' do
context 'when the status removes a verified quote through an implicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
let(:payload) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Note',
summary: 'Show more',
content: 'Hello universe',
}
end
it 'does not remove the quote' do
expect { subject.call(status, json, json) }
.to not_change { status.reload.quote }.from(quote)
end
end
context 'when the status removes a verified quote through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
@ -660,7 +778,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status removes an unverified quote' do
context 'when the status removes an unverified quote through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: nil, state: :pending) }
@ -684,7 +802,44 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status swaps a verified quote with an unverifiable quote' do
context 'when the status swaps a verified quote with an unverifiable quote through an implicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:second_quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
let(:payload) do
{
'@context': [
'https://www.w3.org/ns/activitystreams',
{
'@id': 'https://w3id.org/fep/044f#quote',
'@type': '@id',
},
{
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
'@type': '@id',
},
],
id: 'foo',
type: 'Note',
summary: 'Show more',
content: 'Hello universe',
quote: ActivityPub::TagManager.instance.uri_for(second_quoted_status),
quoteAuthorization: approval_uri,
}
end
it 'does not update the URI or the quote verification status' do
expect { subject.call(status, json, json) }
.to not_change { status.reload.quote }.from(quote)
.and not_change { status.quote.quoted_status }.from(quoted_status)
.and not_change { status.quote.state }.from('accepted')
end
end
context 'when the status swaps a verified quote with an unverifiable quote through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:second_quoted_status) { Fabricate(:status, account: quoted_account) }
@ -752,7 +907,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'when the status swaps a verified quote with another verifiable quote' do
context 'when the status swaps a verified quote with another verifiable quote through an explicit update' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:second_quoted_account) { Fabricate(:account, domain: 'second-quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }