Merge pull request #69 from kmycode/kb-draft-7.0

Bump version to 7.0
This commit is contained in:
KMY(雪あすか) 2023-10-11 12:25:45 +09:00 committed by GitHub
commit 285dc3fcf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 605 additions and 193 deletions

View file

@ -2,7 +2,7 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [4.2.1] - UNRELEASED ## [4.2.1] - 2023-10-10
### Added ### Added
@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file.
### Fixed ### Fixed
- Fix clicking on already-opened thread post scrolling to the top of the thread ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27331), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27338), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27350))
- Fix some remote posts getting truncated ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27307)) - Fix some remote posts getting truncated ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27307))
- Fix some cases of infinite scroll code trying to fetch inaccessible posts in a loop ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27286)) - Fix some cases of infinite scroll code trying to fetch inaccessible posts in a loop ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27286))
- Fix `Vary` headers not being set on some redirects ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27272)) - Fix `Vary` headers not being set on some redirects ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27272))

View file

@ -15,6 +15,7 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
| Version | Supported | | Version | Supported |
| ------- | ---------------- | | ------- | ---------------- |
| 4.2.x | Yes |
| 4.1.x | Yes | | 4.1.x | Yes |
| 4.0.x | Until 2023-10-31 | | 4.0.x | Until 2023-10-31 |
| 3.5.x | Until 2023-12-31 | | 3.5.x | Until 2023-12-31 |

View file

@ -35,7 +35,7 @@ module Admin
def update def update
authorize :friend_server, :update? authorize :friend_server, :update?
if @friend.update(resource_params) if @friend.update(update_resource_params)
redirect_to admin_friend_servers_path redirect_to admin_friend_servers_path
else else
render action: :edit render action: :edit
@ -79,7 +79,11 @@ module Admin
end end
def resource_params def resource_params
params.require(:friend_domain).permit(:domain, :inbox_url, :available, :pseudo_relay, :unlocked, :allow_all_posts) params.require(:friend_domain).permit(:domain, :inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts)
end
def update_resource_params
params.require(:friend_domain).permit(:inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts)
end end
def warn_signatures_not_enabled! def warn_signatures_not_enabled!

View file

@ -475,9 +475,6 @@ class Status extends ImmutablePureComponent {
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */} {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div onClick={this.handleClick} className='status__info'> <div onClick={this.handleClick} className='status__info'>
<a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'> <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
{withReference}
{withExpiration}
{withLimited}
<span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span> <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>} <RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
</a> </a>

View file

@ -233,6 +233,8 @@ class Status extends ImmutablePureComponent {
componentDidMount () { componentDidMount () {
attachFullscreenListener(this.onFullScreenChange); attachFullscreenListener(this.onFullScreenChange);
this._scrollStatusIntoView();
} }
UNSAFE_componentWillReceiveProps (nextProps) { UNSAFE_componentWillReceiveProps (nextProps) {
@ -638,10 +640,10 @@ class Status extends ImmutablePureComponent {
this.node = c; this.node = c;
}; };
componentDidUpdate (prevProps) { _scrollStatusIntoView () {
const { status, ancestorsIds, multiColumn } = this.props; const { status, multiColumn } = this.props;
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) { if (status) {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
this.node?.querySelector('.detailed-status__wrapper')?.scrollIntoView(true); this.node?.querySelector('.detailed-status__wrapper')?.scrollIntoView(true);
@ -658,6 +660,14 @@ class Status extends ImmutablePureComponent {
} }
} }
componentDidUpdate (prevProps) {
const { status, ancestorsIds } = this.props;
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) {
this._scrollStatusIntoView();
}
}
componentWillUnmount () { componentWillUnmount () {
detachFullscreenListener(this.onFullScreenChange); detachFullscreenListener(this.onFullScreenChange);
} }
@ -666,6 +676,22 @@ class Status extends ImmutablePureComponent {
this.setState({ fullscreen: isFullscreen() }); this.setState({ fullscreen: isFullscreen() });
}; };
shouldUpdateScroll = (prevRouterProps, { location }) => {
// Do not change scroll when opening a modal
if (location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey) {
return false;
}
// Scroll to focused post if it is loaded
const child = this.node?.querySelector('.detailed-status__wrapper');
if (child) {
return [0, child.offsetTop];
}
// Do not scroll otherwise, `componentDidUpdate` will take care of that
return false;
};
render () { render () {
let ancestors, descendants, references; let ancestors, descendants, references;
const { isLoading, status, ancestorsIds, descendantsIds, referenceIds, intl, domain, multiColumn, pictureInPicture } = this.props; const { isLoading, status, ancestorsIds, descendantsIds, referenceIds, intl, domain, multiColumn, pictureInPicture } = this.props;
@ -723,7 +749,7 @@ class Status extends ImmutablePureComponent {
)} )}
/> />
<ScrollContainer scrollKey='thread'> <ScrollContainer scrollKey='thread' shouldUpdateScroll={this.shouldUpdateScroll}>
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}> <div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
{references} {references}
{ancestors} {ancestors}

View file

@ -102,7 +102,7 @@
"bundle_modal_error.message": "컴포넌트를 불러오는 중 문제가 발생했습니다.", "bundle_modal_error.message": "컴포넌트를 불러오는 중 문제가 발생했습니다.",
"bundle_modal_error.retry": "다시 시도", "bundle_modal_error.retry": "다시 시도",
"closed_registrations.other_server_instructions": "마스토돈은 분산화 되어 있기 때문에, 다른 서버에서 계정을 만들더라도 이 서버와 상호작용 할 수 있습니다.", "closed_registrations.other_server_instructions": "마스토돈은 분산화 되어 있기 때문에, 다른 서버에서 계정을 만들더라도 이 서버와 상호작용 할 수 있습니다.",
"closed_registrations_modal.description": "{domain}은 현재 가입이 막혀있는 상태입니다, 만약 마스토돈을 이용하기 위해 꼭 {domain}을 사용할 필요는 없다는 사실을 인지해 두세요.", "closed_registrations_modal.description": "{domain}은 현재 가입이 막혀있는 상태입니다, 마스토돈을 이용하기 위해 꼭 {domain}을 사용할 필요는 없다는 사실을 인지해 두세요.",
"closed_registrations_modal.find_another_server": "다른 서버 찾기", "closed_registrations_modal.find_another_server": "다른 서버 찾기",
"closed_registrations_modal.preamble": "마스토돈은 분산화 되어 있습니다, 그렇기 때문에 어디에서 계정을 생성하든, 이 서버에 있는 누구와도 팔로우와 상호작용을 할 수 있습니다. 심지어는 스스로 서버를 만드는 것도 가능합니다!", "closed_registrations_modal.preamble": "마스토돈은 분산화 되어 있습니다, 그렇기 때문에 어디에서 계정을 생성하든, 이 서버에 있는 누구와도 팔로우와 상호작용을 할 수 있습니다. 심지어는 스스로 서버를 만드는 것도 가능합니다!",
"closed_registrations_modal.title": "마스토돈에서 가입", "closed_registrations_modal.title": "마스토돈에서 가입",

View file

@ -312,6 +312,7 @@
"home.hide_announcements": "ကြေညာချက်များကို ဖျောက်ပါ", "home.hide_announcements": "ကြေညာချက်များကို ဖျောက်ပါ",
"home.pending_critical_update.body": "သင့် Mastodon ဆာဗာ အမြန်ဆုံး အပ်ဒိတ်လုပ်ပါ။", "home.pending_critical_update.body": "သင့် Mastodon ဆာဗာ အမြန်ဆုံး အပ်ဒိတ်လုပ်ပါ။",
"home.pending_critical_update.link": "အပ်ဒိတ်များကြည့်ရန်", "home.pending_critical_update.link": "အပ်ဒိတ်များကြည့်ရန်",
"home.pending_critical_update.title": "အရေးကြီးသည့် လုံခြုံရေးအပ်ဒိတ် ရနိုင်ပါမည်။",
"home.show_announcements": "ကြေညာချက်များကို ပြပါ", "home.show_announcements": "ကြေညာချက်များကို ပြပါ",
"interaction_modal.description.favourite": "Mastodon အကောင့်ဖြင့် ဤပို့စ်ကို သင် favorite ပြုလုပ်ကြောင်း စာရေးသူအား အသိပေးပြီး နောက်ပိုင်းတွင် သိမ်းဆည်းနိုင်သည်။", "interaction_modal.description.favourite": "Mastodon အကောင့်ဖြင့် ဤပို့စ်ကို သင် favorite ပြုလုပ်ကြောင်း စာရေးသူအား အသိပေးပြီး နောက်ပိုင်းတွင် သိမ်းဆည်းနိုင်သည်။",
"interaction_modal.description.follow": "Mastodon အကောင့်ဖြင့် သင်၏ ပင်မစာမျက်နှာတွင် ၎င်းတို့၏ ပို့စ်များကို ရရှိရန်အတွက် {name} ကို စောင့်ကြည့်နိုင်ပါသည်။", "interaction_modal.description.follow": "Mastodon အကောင့်ဖြင့် သင်၏ ပင်မစာမျက်နှာတွင် ၎င်းတို့၏ ပို့စ်များကို ရရှိရန်အတွက် {name} ကို စောင့်ကြည့်နိုင်ပါသည်။",
@ -594,6 +595,7 @@
"search_popout.options": "ရွေးချယ်ထားသည်များ ရှာဖွေရန်", "search_popout.options": "ရွေးချယ်ထားသည်များ ရှာဖွေရန်",
"search_popout.quick_actions": "အမြန်လုပ်ဆောင်မှုများ", "search_popout.quick_actions": "အမြန်လုပ်ဆောင်မှုများ",
"search_popout.recent": "လတ်တလော ရှာဖွေမှုများ", "search_popout.recent": "လတ်တလော ရှာဖွေမှုများ",
"search_popout.specific_date": "သီးခြားရက်စွဲ",
"search_popout.user": "အသုံးပြုသူ", "search_popout.user": "အသုံးပြုသူ",
"search_results.accounts": "စာမျက်နှာ", "search_results.accounts": "စာမျက်နှာ",
"search_results.all": "အားလုံး", "search_results.all": "အားလုံး",

View file

@ -6,6 +6,7 @@
"about.rules": "සේවාදායකයේ නීති", "about.rules": "සේවාදායකයේ නීති",
"account.account_note_header": "සටහන", "account.account_note_header": "සටහන",
"account.add_or_remove_from_list": "ලැයිස්තු වලින් එකතු හෝ ඉවත් කරන්න", "account.add_or_remove_from_list": "ලැයිස්තු වලින් එකතු හෝ ඉවත් කරන්න",
"account.badges.bot": "ස්වයංක්‍රියයි",
"account.badges.group": "සමූහය", "account.badges.group": "සමූහය",
"account.block": "@{name} අවහිර කරන්න", "account.block": "@{name} අවහිර කරන්න",
"account.block_domain": "{domain} වසම අවහිර කරන්න", "account.block_domain": "{domain} වසම අවහිර කරන්න",
@ -21,14 +22,15 @@
"account.follow": "අනුගමනය", "account.follow": "අනුගමනය",
"account.followers": "අනුගාමිකයින්", "account.followers": "අනුගාමිකයින්",
"account.followers.empty": "කිසිවෙක් අනුගමනය කර නැත.", "account.followers.empty": "කිසිවෙක් අනුගමනය කර නැත.",
"account.following": "අනුගමනය", "account.followers_counter": "{count, plural, one {අනුගාමිකයින් {counter}} other {අනුගාමිකයින් {counter}}}",
"account.following_counter": "{count, plural, one {අනුගාමිකයින් {counter}} other {අනුගාමිකයින් {counter}}}", "account.following": "අනුගමන",
"account.following_counter": "{count, plural, one {අනුගමන {counter}} other {අනුගමන {counter}}}",
"account.follows.empty": "තවමත් කිසිවෙක් අනුගමනය නොකරයි.", "account.follows.empty": "තවමත් කිසිවෙක් අනුගමනය නොකරයි.",
"account.follows_you": "ඔබව අනුගමනය කරයි", "account.follows_you": "ඔබව අනුගමනය කරයි",
"account.go_to_profile": "පැතිකඩට යන්න", "account.go_to_profile": "පැතිකඩට යන්න",
"account.joined_short": "එක් වූ දිනය", "account.joined_short": "එක් වූ දිනය",
"account.link_verified_on": "මෙම සබැඳියේ අයිතිය {date} දී පරීක්‍ෂා කෙරිණි", "account.link_verified_on": "මෙම සබැඳියේ අයිතිය {date} දී පරීක්‍ෂා කෙරිණි",
"account.media": "මාධ්‍ය", "account.media": "මාධ්‍ය",
"account.mention": "@{name} සඳහන් කරන්ක", "account.mention": "@{name} සඳහන් කරන්ක",
"account.mute": "@{name} නිහඬ කරන්න", "account.mute": "@{name} නිහඬ කරන්න",
"account.mute_short": "නිහඬ", "account.mute_short": "නිහඬ",
@ -37,6 +39,7 @@
"account.posts_with_replies": "ලිපි සහ පිළිතුරු", "account.posts_with_replies": "ලිපි සහ පිළිතුරු",
"account.report": "@{name} වාර්තා කරන්න", "account.report": "@{name} වාර්තා කරන්න",
"account.share": "@{name} ගේ පැතිකඩ බෙදාගන්න", "account.share": "@{name} ගේ පැතිකඩ බෙදාගන්න",
"account.statuses_counter": "{count, plural, one {ලිපි {counter}} other {ලිපි {counter}}}",
"account.unblock": "@{name} අනවහිර කරන්න", "account.unblock": "@{name} අනවහිර කරන්න",
"account.unblock_domain": "{domain} වසම අනවහිර කරන්න", "account.unblock_domain": "{domain} වසම අනවහිර කරන්න",
"account.unblock_short": "අනවහිර", "account.unblock_short": "අනවහිර",
@ -53,11 +56,17 @@
"audio.hide": "හඬපටය සඟවන්න", "audio.hide": "හඬපටය සඟවන්න",
"autosuggest_hashtag.per_week": "සතියකට {count}", "autosuggest_hashtag.per_week": "සතියකට {count}",
"boost_modal.combo": "ඊළඟ වතාවේ මෙය මඟ හැරීමට {combo} එබීමට හැකිය", "boost_modal.combo": "ඊළඟ වතාවේ මෙය මඟ හැරීමට {combo} එබීමට හැකිය",
"bundle_column_error.copy_stacktrace": "දෝෂ වාර්තාවේ පිටපතක්",
"bundle_column_error.error.title": "අපොයි!",
"bundle_column_error.network.title": "ජාලයේ දෝෂයකි",
"bundle_column_error.retry": "නැවත උත්සාහ කරන්න", "bundle_column_error.retry": "නැවත උත්සාහ කරන්න",
"bundle_column_error.return": "ආපසු මුලට යන්න",
"bundle_column_error.routing.title": "404",
"bundle_modal_error.close": "වසන්න", "bundle_modal_error.close": "වසන්න",
"bundle_modal_error.message": "මෙම සංරචකය පූරණයේ දී යම් දෙයක් වැරදී ඇත.", "bundle_modal_error.message": "මෙම සංරචකය පූරණයේ දී යම් දෙයක් වැරදී ඇත.",
"bundle_modal_error.retry": "නැවත උත්සාහ කරන්න", "bundle_modal_error.retry": "නැවත උත්සාහ කරන්න",
"closed_registrations_modal.find_another_server": "වෙනත් සේවාදායක", "closed_registrations_modal.find_another_server": "වෙනත් සේවාදායක",
"closed_registrations_modal.title": "මාස්ටඩන් හි ලියාපදිංචි වන්න",
"column.about": "පිලිබඳව", "column.about": "පිලිබඳව",
"column.blocks": "අවහිර කළ අය", "column.blocks": "අවහිර කළ අය",
"column.bookmarks": "පොත්යොමු", "column.bookmarks": "පොත්යොමු",
@ -159,7 +168,7 @@
"empty_column.domain_blocks": "අවහිර කරන ලද වසම් නැත.", "empty_column.domain_blocks": "අවහිර කරන ලද වසම් නැත.",
"empty_column.explore_statuses": "දැන් කිසිවක් නැඹුරු නොවේ. පසුව නැවත පරීක්ෂා කරන්න!", "empty_column.explore_statuses": "දැන් කිසිවක් නැඹුරු නොවේ. පසුව නැවත පරීක්ෂා කරන්න!",
"empty_column.follow_requests": "ඔබට තවමත් අනුගමන ඉල්ලීම් ලැබී නැත. ඉල්ලීමක් ලැබුණු විට, එය මෙහි පෙන්වනු ඇත.", "empty_column.follow_requests": "ඔබට තවමත් අනුගමන ඉල්ලීම් ලැබී නැත. ඉල්ලීමක් ලැබුණු විට, එය මෙහි පෙන්වනු ඇත.",
"empty_column.home": "ඔබගේ මුල් පිටුව හිස් ය! මෙය පිරවීමට බොහෝ පුද්ගලයින් අනුගමනය කරන්න.", "empty_column.home": "මුල් පිටුව හිස් ය! මෙය පිරවීමට බොහෝ පුද්ගලයින් අනුගමනය කරන්න.",
"empty_column.lists": "ඔබට තවමත් ලැයිස්තු කිසිවක් නැත. ඔබ එකක් සාදන විට, එය මෙහි පෙන්වනු ඇත.", "empty_column.lists": "ඔබට තවමත් ලැයිස්තු කිසිවක් නැත. ඔබ එකක් සාදන විට, එය මෙහි පෙන්වනු ඇත.",
"empty_column.mutes": "ඔබ තවමත් කිසිදු පරිශීලකයෙකු නිහඬ කර නැත.", "empty_column.mutes": "ඔබ තවමත් කිසිදු පරිශීලකයෙකු නිහඬ කර නැත.",
"empty_column.notifications": "ඔබට දැනුම්දීම් ලැබී නැත. අන් අය සහ ඔබ අතර අන්‍යෝන්‍ය බලපවත්වන දෑ මෙහි දිස්වනු ඇත.", "empty_column.notifications": "ඔබට දැනුම්දීම් ලැබී නැත. අන් අය සහ ඔබ අතර අන්‍යෝන්‍ය බලපවත්වන දෑ මෙහි දිස්වනු ඇත.",
@ -264,7 +273,7 @@
"navigation_bar.favourites": "ප්‍රියතමයන්", "navigation_bar.favourites": "ප්‍රියතමයන්",
"navigation_bar.filters": "නිහඬ කළ වචන", "navigation_bar.filters": "නිහඬ කළ වචන",
"navigation_bar.follow_requests": "අනුගමන ඉල්ලීම්", "navigation_bar.follow_requests": "අනුගමන ඉල්ලීම්",
"navigation_bar.follows_and_followers": "අනුගමන හා අනුගාමිකයින්", "navigation_bar.follows_and_followers": "අනුගමන හා අනුගාමික",
"navigation_bar.lists": "ලැයිස්තු", "navigation_bar.lists": "ලැයිස්තු",
"navigation_bar.logout": "නික්මෙන්න", "navigation_bar.logout": "නික්මෙන්න",
"navigation_bar.mutes": "නිහඬ කළ අය", "navigation_bar.mutes": "නිහඬ කළ අය",
@ -445,7 +454,7 @@
"time_remaining.seconds": "{number, plural, one {තත්පර #} other {තත්පර #}} ක් ඉතිරිය", "time_remaining.seconds": "{number, plural, one {තත්පර #} other {තත්පර #}} ක් ඉතිරිය",
"timeline_hint.remote_resource_not_displayed": "වෙනත් සේවාදායකයන්ගෙන් {resource} දර්ශනය නොවේ.", "timeline_hint.remote_resource_not_displayed": "වෙනත් සේවාදායකයන්ගෙන් {resource} දර්ශනය නොවේ.",
"timeline_hint.resources.followers": "අනුගාමිකයින්", "timeline_hint.resources.followers": "අනුගාමිකයින්",
"timeline_hint.resources.follows": "අනුගමන", "timeline_hint.resources.follows": "අනුගමන",
"timeline_hint.resources.statuses": "පරණ ලිපි", "timeline_hint.resources.statuses": "පරණ ලිපි",
"trends.trending_now": "දැන් නැගී එන", "trends.trending_now": "දැන් නැගී එන",
"ui.beforeunload": "ඔබ මාස්ටඩන් හැර ගියහොත් කටුපිටපත අහිමි වේ.", "ui.beforeunload": "ඔබ මාස්ටඩන් හැර ගියහොත් කටුපිටපත අහිමි වේ.",

View file

@ -79,16 +79,16 @@
"admin.impact_report.instance_accounts": "Профілі облікових записів буде видалено", "admin.impact_report.instance_accounts": "Профілі облікових записів буде видалено",
"admin.impact_report.instance_followers": "Підписники, яких можуть втратити наші користувачі", "admin.impact_report.instance_followers": "Підписники, яких можуть втратити наші користувачі",
"admin.impact_report.instance_follows": "Підписники, яких можуть втратити їхні користувачі", "admin.impact_report.instance_follows": "Підписники, яких можуть втратити їхні користувачі",
"admin.impact_report.title": "Наслідки", "admin.impact_report.title": "Підсумки впливу",
"alert.rate_limited.message": "Спробуйте ще раз через {retry_time, time, medium}.", "alert.rate_limited.message": "Спробуйте ще раз за {retry_time, time, medium}.",
"alert.rate_limited.title": "Швидкість обмежена", "alert.rate_limited.title": "Швидкість обмежена",
"alert.unexpected.message": "Сталася неочікувана помилка.", "alert.unexpected.message": "Сталася неочікувана помилка.",
"alert.unexpected.title": "Ой!", "alert.unexpected.title": "Ой!",
"announcement.announcement": "Оголошення", "announcement.announcement": "Оголошення",
"attachments_list.unprocessed": "(не оброблено)", "attachments_list.unprocessed": "(не оброблено)",
"audio.hide": "Сховати аудіо", "audio.hide": "Сховати аудіо",
"autosuggest_hashtag.per_week": "{count} в тиждень", "autosuggest_hashtag.per_week": "{count} на тиждень",
"boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу", "boost_modal.combo": "Ви можете натиснути {combo}, щоби пропустити це наступного разу",
"bundle_column_error.copy_stacktrace": "Копіювати звіт про помилку", "bundle_column_error.copy_stacktrace": "Копіювати звіт про помилку",
"bundle_column_error.error.body": "Неможливо показати запитану сторінку. Це може бути спричинено помилкою у нашому коді, або через проблему сумісності з браузером.", "bundle_column_error.error.body": "Неможливо показати запитану сторінку. Це може бути спричинено помилкою у нашому коді, або через проблему сумісності з браузером.",
"bundle_column_error.error.title": "О, ні!", "bundle_column_error.error.title": "О, ні!",
@ -140,7 +140,7 @@
"compose.saved.body": "Допис збережено.", "compose.saved.body": "Допис збережено.",
"compose_form.direct_message_warning_learn_more": "Дізнатися більше", "compose_form.direct_message_warning_learn_more": "Дізнатися більше",
"compose_form.encryption_warning": "Дописи на Mastodon не захищені шифруванням. Не поширюйте жодну делікатну інформацію.", "compose_form.encryption_warning": "Дописи на Mastodon не захищені шифруванням. Не поширюйте жодну делікатну інформацію.",
"compose_form.hashtag_warning": ей допис не буде зображений у жодній стрічці гештеґу, оскільки він прихований. Тільки публічні дописи можуть бути знайдені за гештеґом.", "compose_form.hashtag_warning": ього допису не буде під жодним гештеґом, оскільки він не є загальнодоступним. За гештеґом можна шукати лише публічні дописи.",
"compose_form.lock_disclaimer": "Ваш обліковий запис не {locked}. Будь-який користувач може підписатися на вас та переглядати ваші дописи для підписників.", "compose_form.lock_disclaimer": "Ваш обліковий запис не {locked}. Будь-який користувач може підписатися на вас та переглядати ваші дописи для підписників.",
"compose_form.lock_disclaimer.lock": "приватний", "compose_form.lock_disclaimer.lock": "приватний",
"compose_form.placeholder": "Що у вас на думці?", "compose_form.placeholder": "Що у вас на думці?",
@ -151,7 +151,7 @@
"compose_form.poll.switch_to_multiple": "Дозволити вибір декількох відповідей", "compose_form.poll.switch_to_multiple": "Дозволити вибір декількох відповідей",
"compose_form.poll.switch_to_single": "Перемкнути у режим вибору однієї відповіді", "compose_form.poll.switch_to_single": "Перемкнути у режим вибору однієї відповіді",
"compose_form.publish": "Опублікувати", "compose_form.publish": "Опублікувати",
"compose_form.publish_form": "Опублікувати", "compose_form.publish_form": "Новий допис",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Зберегти зміни", "compose_form.save_changes": "Зберегти зміни",
"compose_form.sensitive.hide": "{count, plural, one {Позначити медіа делікатним} other {Позначити медіа делікатними}}", "compose_form.sensitive.hide": "{count, plural, one {Позначити медіа делікатним} other {Позначити медіа делікатними}}",
@ -206,7 +206,7 @@
"dismissable_banner.explore_tags": "Ці хештеги зараз набирають популярності серед людей на цьому та інших серверах децентралізованої мережі. Хештеги, які використовуються більшою кількістю людей, мають вищий рейтинг.", "dismissable_banner.explore_tags": "Ці хештеги зараз набирають популярності серед людей на цьому та інших серверах децентралізованої мережі. Хештеги, які використовуються більшою кількістю людей, мають вищий рейтинг.",
"dismissable_banner.public_timeline": "Це найновіші загальнодоступні дописи від людей в соціальній мережі, на які підписані люди в {domain}.", "dismissable_banner.public_timeline": "Це найновіші загальнодоступні дописи від людей в соціальній мережі, на які підписані люди в {domain}.",
"embed.instructions": "Вбудуйте цей допис до вашого вебсайту, скопіювавши код нижче.", "embed.instructions": "Вбудуйте цей допис до вашого вебсайту, скопіювавши код нижче.",
"embed.preview": "Ось як він виглядатиме:", "embed.preview": "Ось який вигляд це матиме:",
"emoji_button.activity": "Діяльність", "emoji_button.activity": "Діяльність",
"emoji_button.clear": "Очистити", "emoji_button.clear": "Очистити",
"emoji_button.custom": "Власні", "emoji_button.custom": "Власні",
@ -227,7 +227,7 @@
"empty_column.account_unavailable": "Профіль недоступний", "empty_column.account_unavailable": "Профіль недоступний",
"empty_column.blocks": "Ви ще не заблокували жодного користувача.", "empty_column.blocks": "Ви ще не заблокували жодного користувача.",
"empty_column.bookmarked_statuses": "У вас ще немає дописів у закладках. Коли ви щось додасте до закладок, воно з'явиться тут.", "empty_column.bookmarked_statuses": "У вас ще немає дописів у закладках. Коли ви щось додасте до закладок, воно з'явиться тут.",
"empty_column.community": "Локальна стрічка пуста. Напишіть щось, щоб розігріти народ!", "empty_column.community": "Локальна стрічка порожня. Напишіть щось, щоб розігріти народ!",
"empty_column.direct": "У вас ще немає жодних особистих згадок. Коли ви надсилаєте чи отримуєте повідомлення, воно з'явиться тут.", "empty_column.direct": "У вас ще немає жодних особистих згадок. Коли ви надсилаєте чи отримуєте повідомлення, воно з'явиться тут.",
"empty_column.domain_blocks": "Тут поки немає прихованих доменів.", "empty_column.domain_blocks": "Тут поки немає прихованих доменів.",
"empty_column.explore_statuses": "Нема нічого популярного. Подивіться пізніше!", "empty_column.explore_statuses": "Нема нічого популярного. Подивіться пізніше!",
@ -240,10 +240,10 @@
"empty_column.list": "Цей список порожній. Коли його учасники додадуть нові дописи, вони з'являться тут.", "empty_column.list": "Цей список порожній. Коли його учасники додадуть нові дописи, вони з'являться тут.",
"empty_column.lists": "У вас ще немає списків. Коли ви їх створите, вони з'являться тут.", "empty_column.lists": "У вас ще немає списків. Коли ви їх створите, вони з'являться тут.",
"empty_column.mutes": "Ви ще не приховали жодного користувача.", "empty_column.mutes": "Ви ще не приховали жодного користувача.",
"empty_column.notifications": "У вас ще немає сповіщень. Переписуйтесь з іншими користувачами, щоб почати розмову.", "empty_column.notifications": "У вас ще немає сповіщень. Коли інші люди почнуть взаємодіяти з вами, ви побачите їх тут.",
"empty_column.public": "Тут поки нічого немає! Опублікуйте щось, або вручну підпишіться на користувачів інших серверів, щоб заповнити стрічку", "empty_column.public": "Тут поки нічого немає! Опублікуйте щось, або вручну підпишіться на користувачів інших серверів, щоб заповнити стрічку",
"error.unexpected_crash.explanation": "Через помилку у нашому коді або несумісність браузера, ця сторінка не може бути зображена коректно.", "error.unexpected_crash.explanation": "Через помилку у нашому коді або несумісність браузера, ця сторінка не може бути зображена коректно.",
"error.unexpected_crash.explanation_addons": "Неможливо правильно показати цю сторінку. Ймовірно, цю помилку викликано додатком браузера або автоматичним засобом перекладу.", "error.unexpected_crash.explanation_addons": "Неможливо правильно показати цю сторінку. Ймовірно, цю помилку спричинило розширення браузера або автоматичний засіб перекладу.",
"error.unexpected_crash.next_steps": "Спробуйте перезавантажити сторінку. Якщо це не допоможе, ви все ще зможете використовувати Mastodon через інший браузер або рідний застосунок.", "error.unexpected_crash.next_steps": "Спробуйте перезавантажити сторінку. Якщо це не допоможе, ви все ще зможете використовувати Mastodon через інший браузер або рідний застосунок.",
"error.unexpected_crash.next_steps_addons": "Спробуйте їх вимкнути та оновити сторінку. Якщо це не допомагає, ви можете використовувати Mastodon через інший браузер або окремий застосунок.", "error.unexpected_crash.next_steps_addons": "Спробуйте їх вимкнути та оновити сторінку. Якщо це не допомагає, ви можете використовувати Mastodon через інший браузер або окремий застосунок.",
"errors.unexpected_crash.copy_stacktrace": "Скопіювати трасування стека у буфер обміну", "errors.unexpected_crash.copy_stacktrace": "Скопіювати трасування стека у буфер обміну",
@ -394,7 +394,7 @@
"moved_to_account_banner.text": "Ваш обліковий запис {disabledAccount} наразі вимкнений, оскільки вас перенесено до {movedToAccount}.", "moved_to_account_banner.text": "Ваш обліковий запис {disabledAccount} наразі вимкнений, оскільки вас перенесено до {movedToAccount}.",
"mute_modal.duration": "Тривалість", "mute_modal.duration": "Тривалість",
"mute_modal.hide_notifications": "Сховати сповіщення цього користувача?", "mute_modal.hide_notifications": "Сховати сповіщення цього користувача?",
"mute_modal.indefinite": "Назавжди", "mute_modal.indefinite": "Невизначений строк",
"navigation_bar.about": "Про застосунок", "navigation_bar.about": "Про застосунок",
"navigation_bar.advanced_interface": "Відкрити в розширеному вебінтерфейсі", "navigation_bar.advanced_interface": "Відкрити в розширеному вебінтерфейсі",
"navigation_bar.blocks": "Заблоковані користувачі", "navigation_bar.blocks": "Заблоковані користувачі",
@ -428,7 +428,7 @@
"notification.follow": "{name} підписалися на вас", "notification.follow": "{name} підписалися на вас",
"notification.follow_request": "{name} відправили запит на підписку", "notification.follow_request": "{name} відправили запит на підписку",
"notification.mention": "{name} згадали вас", "notification.mention": "{name} згадали вас",
"notification.own_poll": "Ваше опитування завершено", "notification.own_poll": "Ваше опитування завершилося",
"notification.poll": "Опитування, у якому ви голосували, скінчилося", "notification.poll": "Опитування, у якому ви голосували, скінчилося",
"notification.reblog": "{name} поширює ваш допис", "notification.reblog": "{name} поширює ваш допис",
"notification.status": "{name} щойно дописує", "notification.status": "{name} щойно дописує",
@ -480,7 +480,7 @@
"onboarding.follows.title": "Персоналізуйте домашню стрічку", "onboarding.follows.title": "Персоналізуйте домашню стрічку",
"onboarding.share.lead": "Розкажіть людям про те, як вони можуть знайти вас на Mastodon!", "onboarding.share.lead": "Розкажіть людям про те, як вони можуть знайти вас на Mastodon!",
"onboarding.share.message": "Я {username} на #Mastodon! Стежте за мною на {url}", "onboarding.share.message": "Я {username} на #Mastodon! Стежте за мною на {url}",
"onboarding.share.next_steps": "Можливі наступні кроки:", "onboarding.share.next_steps": "Можливі такі кроки:",
"onboarding.share.title": "Поділитися своїм профілем", "onboarding.share.title": "Поділитися своїм профілем",
"onboarding.start.lead": "Тепер ви — частина Mastodon, унікальної децентралізованої платформи соціальних медіа, де ви, а не алгоритми керують вашими вподобаннями. Розпочнімо роботу:", "onboarding.start.lead": "Тепер ви — частина Mastodon, унікальної децентралізованої платформи соціальних медіа, де ви, а не алгоритми керують вашими вподобаннями. Розпочнімо роботу:",
"onboarding.start.skip": "Хочете пропустити?", "onboarding.start.skip": "Хочете пропустити?",
@ -694,7 +694,7 @@
"units.short.thousand": "{count} тис", "units.short.thousand": "{count} тис",
"upload_area.title": "Перетягніть сюди, щоб завантажити", "upload_area.title": "Перетягніть сюди, щоб завантажити",
"upload_button.label": "Додати зображення, відео або аудіо", "upload_button.label": "Додати зображення, відео або аудіо",
"upload_error.limit": "Ліміт завантаження файлів перевищено.", "upload_error.limit": "Ви перевищили ліміт завантаження файлів.",
"upload_error.poll": "Не можна завантажувати файли до опитувань.", "upload_error.poll": "Не можна завантажувати файли до опитувань.",
"upload_form.audio_description": "Опишіть для людей із вадами слуху", "upload_form.audio_description": "Опишіть для людей із вадами слуху",
"upload_form.description": "Опишіть для людей з вадами зору", "upload_form.description": "Опишіть для людей з вадами зору",
@ -713,7 +713,7 @@
"upload_modal.hint": "Клацніть або перетягніть коло на превʼю, щоб обрати точку, яку буде завжди видно на мініатюрах.", "upload_modal.hint": "Клацніть або перетягніть коло на превʼю, щоб обрати точку, яку буде завжди видно на мініатюрах.",
"upload_modal.preparing_ocr": "Підготовка OCR…", "upload_modal.preparing_ocr": "Підготовка OCR…",
"upload_modal.preview_label": "Переглянути ({ratio})", "upload_modal.preview_label": "Переглянути ({ratio})",
"upload_progress.label": "Завантаження...", "upload_progress.label": "Вивантаження...",
"upload_progress.processing": "Обробка…", "upload_progress.processing": "Обробка…",
"username.taken": "Це ім'я користувача вже зайнято. Спробуйте інше", "username.taken": "Це ім'я користувача вже зайнято. Спробуйте інше",
"video.close": "Закрити відео", "video.close": "Закрити відео",

View file

@ -45,7 +45,7 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
end end
def accept_follow_for_friend def accept_follow_for_friend
friend.update!(active_state: :accepted) friend.update!(active_state: :accepted, passive_state: :idle)
end end
def friend def friend

View file

@ -114,7 +114,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end end
def process_status_params def process_status_params
@status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: @account.followers_url, object: @object, account: @account) @status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: @account.followers_url, object: @object, account: @account, friend_domain: friend_domain?)
@params = { @params = {
uri: @status_parser.uri, uri: @status_parser.uri,
@ -506,6 +506,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
FriendDomain.free_receivings.exists?(domain: @account.domain) FriendDomain.free_receivings.exists?(domain: @account.domain)
end end
def friend_domain?
FriendDomain.enabled.find_by(domain: @account.domain)&.accepted?
end
def quote def quote
@quote ||= @object['quote'] || @object['quoteUrl'] || @object['quoteURL'] || @object['_misskey_quote'] @quote ||= @object['quote'] || @object['quoteUrl'] || @object['quoteURL'] || @object['_misskey_quote']
end end

View file

@ -49,16 +49,20 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity
already_accepted = false already_accepted = false
if friend.present? if friend.present?
already_accepted = friend.they_are_accepted? already_accepted = friend.accepted?
friend.update!(passive_state: :pending, passive_follow_activity_id: @json['id']) friend.update!(passive_state: :pending, active_state: :idle, passive_follow_activity_id: @json['id'])
else else
@friend = FriendDomain.create!(domain: @account.domain, passive_state: :pending, passive_follow_activity_id: @json['id']) @friend = FriendDomain.new(domain: @account.domain, passive_state: :pending, passive_follow_activity_id: @json['id'])
@friend.initialize_inbox_url!
@friend.save!
end end
if already_accepted || friend.unlocked || Setting.unlocked_friend if already_accepted || Setting.unlocked_friend
friend.accept! friend.accept!
else
# Notify for admin even if unlocked # Notify for admin even if unlocked
notify_staff_about_pending_friend_server! unless already_accepted
else
notify_staff_about_pending_friend_server! notify_staff_about_pending_friend_server!
end end
end end

View file

@ -57,6 +57,7 @@ class ActivityPub::Activity::Like < ActivityPub::Activity
NotifyService.new.call(@original_status.account, :emoji_reaction, reaction) NotifyService.new.call(@original_status.account, :emoji_reaction, reaction)
forward_for_emoji_reaction forward_for_emoji_reaction
relay_for_emoji_reaction relay_for_emoji_reaction
relay_friend_for_emoji_reaction
end end
rescue Seahorse::Client::NetworkingError rescue Seahorse::Client::NetworkingError
nil nil
@ -76,6 +77,14 @@ class ActivityPub::Activity::Like < ActivityPub::Activity
end end
end end
def relay_friend_for_emoji_reaction
return unless @json['signature'].present? && @original_status.distributable_friend?
ActivityPub::DeliveryWorker.push_bulk(FriendDomain.distributables.pluck(:inbox_url)) do |inbox_url|
[Oj.dump(@json), @original_status.account.id, inbox_url]
end
end
def shortcode def shortcode
return @shortcode if defined?(@shortcode) return @shortcode if defined?(@shortcode)

View file

@ -39,7 +39,7 @@ class ActivityPub::Activity::Reject < ActivityPub::Activity
end end
def reject_follow_for_friend def reject_follow_for_friend
friend.update!(active_state: :rejected) friend.update!(active_state: :rejected, passive_state: :idle)
end end
def friend def friend

View file

@ -103,7 +103,7 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
end end
def remove_follow_from_friend def remove_follow_from_friend
friend.update!(passive_state: :idle, passive_follow_activity_id: nil) friend.destroy_without_signal!
end end
def friend def friend
@ -146,6 +146,7 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
if @original_status.account.local? if @original_status.account.local?
forward_for_undo_emoji_reaction forward_for_undo_emoji_reaction
relay_for_undo_emoji_reaction relay_for_undo_emoji_reaction
relay_friend_for_undo_emoji_reaction
end end
end end
else else
@ -184,6 +185,14 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
end end
end end
def relay_friend_for_undo_emoji_reaction
return unless @json['signature'].present? && @original_status.distributable_friend?
ActivityPub::DeliveryWorker.push_bulk(FriendDomain.distributables.pluck(:inbox_url)) do |inbox_url|
[Oj.dump(@json), @original_status.account.id, inbox_url]
end
end
def shortcode def shortcode
return @shortcode if defined?(@shortcode) return @shortcode if defined?(@shortcode)

View file

@ -11,6 +11,7 @@ class ActivityPub::Parser::StatusParser
@object = magic_values[:object] || json['object'] || json @object = magic_values[:object] || json['object'] || json
@magic_values = magic_values @magic_values = magic_values
@account = magic_values[:account] @account = magic_values[:account]
@friend = magic_values[:friend_domain]
end end
def uri def uri
@ -76,11 +77,11 @@ class ActivityPub::Parser::StatusParser
def visibility def visibility
if audience_to.any? { |to| ActivityPub::TagManager.instance.public_collection?(to) } if audience_to.any? { |to| ActivityPub::TagManager.instance.public_collection?(to) }
:public :public
elsif audience_to.include?('LocalPublic') elsif audience_to.include?('kmyblue:LocalPublic') && @friend
:public_unlisted :public_unlisted
elsif audience_cc.any? { |cc| ActivityPub::TagManager.instance.public_collection?(cc) } elsif audience_cc.any? { |cc| ActivityPub::TagManager.instance.public_collection?(cc) }
:unlisted :unlisted
elsif audience_to.include?('as:LoginOnly') || audience_to.include?('LoginUser') elsif audience_to.include?('kmyblue:LoginOnly') || audience_to.include?('as:LoginOnly') || audience_to.include?('LoginUser')
:login :login
elsif audience_to.include?(@magic_values[:followers_collection]) elsif audience_to.include?(@magic_values[:followers_collection])
:private :private
@ -198,9 +199,9 @@ class ActivityPub::Parser::StatusParser
nil nil
elsif audience_searchable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) } elsif audience_searchable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) }
:public :public
elsif audience_searchable_by.include?('as:Limited') elsif audience_searchable_by.include?('kmyblue:Limited') || audience_searchable_by.include?('as:Limited')
:limited :limited
elsif audience_searchable_by.include?('LocalPublic') elsif audience_searchable_by.include?('kmyblue:LocalPublic') && @friend
:public_unlisted :public_unlisted
elsif audience_searchable_by.include?(@account.followers_url) elsif audience_searchable_by.include?(@account.followers_url)
:private :private

View file

@ -99,7 +99,7 @@ class ActivityPub::TagManager
when 'unlisted', 'public_unlisted', 'private' when 'unlisted', 'public_unlisted', 'private'
[account_followers_url(status.account)] [account_followers_url(status.account)]
when 'login' when 'login'
[account_followers_url(status.account), 'as:LoginOnly', 'LoginUser'] [account_followers_url(status.account), 'as:LoginOnly', 'kmyblue:LoginOnly', 'LoginUser']
when 'direct' when 'direct'
if status.account.silenced? if status.account.silenced?
# Only notify followers if the account is locally silenced # Only notify followers if the account is locally silenced
@ -128,7 +128,7 @@ class ActivityPub::TagManager
def to_for_friend(status) def to_for_friend(status)
to = to(status) to = to(status)
to << 'LocalPublic' if status.public_unlisted_visibility? to << 'kmyblue:LocalPublic' if status.public_unlisted_visibility?
to to
end end
@ -249,7 +249,7 @@ class ActivityPub::TagManager
when 'direct' when 'direct'
status.conversation_id.present? ? [uri_for(status.conversation)] : [] status.conversation_id.present? ? [uri_for(status.conversation)] : []
when 'limited' when 'limited'
['as:Limited'] ['as:Limited', 'kmyblue:Limited']
else else
[] []
end end
@ -259,7 +259,7 @@ class ActivityPub::TagManager
def searchable_by_for_friend(status) def searchable_by_for_friend(status)
searchable = searchable_by(status) searchable = searchable_by(status)
searchable << 'LocalPublic' if status.compute_searchability_local == 'public_unlisted' searchable << 'kmyblue:LocalPublic' if status.compute_searchability_local == 'public_unlisted'
searchable searchable
end end
@ -270,7 +270,7 @@ class ActivityPub::TagManager
when 'private', 'direct' when 'private', 'direct'
[account_followers_url(account)] [account_followers_url(account)]
when 'limited' when 'limited'
['as:Limited'] ['as:Limited', 'kmyblue:Limited']
else else
[] []
end end

View file

@ -10,7 +10,7 @@ class StatusReachFinder
end end
def inboxes def inboxes
(reached_account_inboxes + followers_inboxes + relay_inboxes).uniq (reached_account_inboxes + followers_inboxes + relay_inboxes + nolocal_friend_inboxes).uniq
end end
def inboxes_for_misskey def inboxes_for_misskey
@ -147,7 +147,15 @@ class StatusReachFinder
def friend_inboxes def friend_inboxes
if @status.public_visibility? || @status.public_unlisted_visibility? || (@status.unlisted_visibility? && (@status.public_searchability? || @status.public_unlisted_searchability?)) if @status.public_visibility? || @status.public_unlisted_visibility? || (@status.unlisted_visibility? && (@status.public_searchability? || @status.public_unlisted_searchability?))
DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.pluck(:inbox_url)) DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.where(delivery_local: true).pluck(:inbox_url))
else
[]
end
end
def nolocal_friend_inboxes
if @status.public_visibility?
DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.where(delivery_local: false).pluck(:inbox_url))
else else
[] []
end end

View file

@ -43,6 +43,10 @@ class EmojiReaction < ApplicationRecord
custom_emoji? && !custom_emoji.local? custom_emoji? && !custom_emoji.local?
end end
def sign?
status&.distributable_friend?
end
private private
def refresh_cache def refresh_cache

View file

@ -13,10 +13,10 @@
# passive_follow_activity_id :string # passive_follow_activity_id :string
# available :boolean default(TRUE), not null # available :boolean default(TRUE), not null
# pseudo_relay :boolean default(FALSE), not null # pseudo_relay :boolean default(FALSE), not null
# unlocked :boolean default(FALSE), not null
# allow_all_posts :boolean default(TRUE), not null # allow_all_posts :boolean default(TRUE), not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# delivery_local :boolean default(TRUE), not null
# #
class FriendDomain < ApplicationRecord class FriendDomain < ApplicationRecord
@ -27,24 +27,31 @@ class FriendDomain < ApplicationRecord
enum passive_state: { idle: 0, pending: 1, accepted: 2, rejected: 3 }, _prefix: :they_are enum passive_state: { idle: 0, pending: 1, accepted: 2, rejected: 3 }, _prefix: :they_are
scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) } scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) }
scope :enabled, -> { where(available: true) } scope :enabled, -> { where(active_state: :accepted).or(FriendDomain.where(passive_state: :accepted)).where(available: true) }
scope :mutuals, -> { enabled.where(active_state: :accepted, passive_state: :accepted) } scope :distributables, -> { enabled.where(pseudo_relay: true) }
scope :distributables, -> { mutuals.where(pseudo_relay: true) } scope :deliver_locals, -> { enabled.where(delivery_local: true) }
scope :deliver_locals, -> { enabled.where(active_state: :accepted) } scope :free_receivings, -> { enabled.where(allow_all_posts: true) }
scope :free_receivings, -> { mutuals.where(allow_all_posts: true) }
before_destroy :ensure_disabled before_destroy :ensure_disabled
after_commit :set_default_inbox_url after_commit :set_default_inbox_url
def mutual? def accepted?
i_am_accepted? && they_are_accepted? i_am_accepted? || they_are_accepted?
end
def pending?
!accepted? && (i_am_pending? || they_are_pending?)
end
def idle?
(i_am_idle? || i_am_rejected?) && (they_are_idle? || they_are_rejected?)
end end
def follow! def follow!
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil)
payload = Oj.dump(follow_activity(activity_id)) payload = Oj.dump(follow_activity(activity_id))
update!(active_state: :pending, active_follow_activity_id: activity_id) update!(active_state: :pending, passive_state: :idle, active_follow_activity_id: activity_id)
DeliveryFailureTracker.reset!(inbox_url) DeliveryFailureTracker.reset!(inbox_url)
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
end end
@ -53,7 +60,7 @@ class FriendDomain < ApplicationRecord
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil)
payload = Oj.dump(unfollow_activity(activity_id)) payload = Oj.dump(unfollow_activity(activity_id))
update!(active_state: :idle, active_follow_activity_id: nil) update!(active_state: :idle, passive_state: :idle, active_follow_activity_id: nil)
DeliveryFailureTracker.reset!(inbox_url) DeliveryFailureTracker.reset!(inbox_url)
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
end end
@ -64,7 +71,7 @@ class FriendDomain < ApplicationRecord
activity_id = passive_follow_activity_id activity_id = passive_follow_activity_id
payload = Oj.dump(accept_follow_activity(activity_id)) payload = Oj.dump(accept_follow_activity(activity_id))
update!(passive_state: :accepted) update!(passive_state: :accepted, active_state: :idle)
DeliveryFailureTracker.reset!(inbox_url) DeliveryFailureTracker.reset!(inbox_url)
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
end end
@ -75,11 +82,21 @@ class FriendDomain < ApplicationRecord
activity_id = passive_follow_activity_id activity_id = passive_follow_activity_id
payload = Oj.dump(reject_follow_activity(activity_id)) payload = Oj.dump(reject_follow_activity(activity_id))
update!(passive_state: :rejected, passive_follow_activity_id: nil) update!(passive_state: :rejected, active_state: :idle, passive_follow_activity_id: nil)
DeliveryFailureTracker.reset!(inbox_url) DeliveryFailureTracker.reset!(inbox_url)
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
end end
def destroy_without_signal!
self.active_state = :idle
self.passive_state = :idle
destroy!
end
def initialize_inbox_url!
self.inbox_url = default_inbox_url
end
private private
def default_inbox_url def default_inbox_url
@ -154,7 +171,7 @@ class FriendDomain < ApplicationRecord
end end
def ensure_disabled def ensure_disabled
delete_for_friend! unless i_am_idle? && they_are_idle? delete_for_friend! unless id.nil? || (i_am_idle? && they_are_idle?)
end end
def set_default_inbox_url def set_default_inbox_url

View file

@ -591,6 +591,10 @@ class Status < ApplicationRecord
end end
end end
def distributable_friend?
public_visibility? || public_unlisted_visibility? || (unlisted_visibility? && (public_searchability? || public_unlisted_searchability?))
end
private private
def update_status_stat!(attrs) def update_status_stat!(attrs)

View file

@ -25,7 +25,7 @@ class UserSettings
setting :default_privacy, default: nil, in: %w(public public_unlisted login unlisted private) setting :default_privacy, default: nil, in: %w(public public_unlisted login unlisted private)
setting :stay_privacy, default: false setting :stay_privacy, default: false
setting :default_reblog_privacy, default: nil setting :default_reblog_privacy, default: nil
setting :default_searchability, default: :direct, in: %w(public private direct limited) setting :default_searchability, default: :direct, in: %w(public private direct limited public_unlisted)
setting :default_searchability_of_search, default: :public, in: %w(public private direct limited) setting :default_searchability_of_search, default: :public, in: %w(public private direct limited)
setting :use_public_index, default: true setting :use_public_index, default: true
setting :disallow_unlisted_public_searchability, default: false setting :disallow_unlisted_public_searchability, default: false

View file

@ -272,7 +272,7 @@ class ActivityPub::ProcessAccountService < BaseService
:public :public
elsif audience_searchable_by.include?(@account.followers_url) elsif audience_searchable_by.include?(@account.followers_url)
:private :private
elsif audience_searchable_by.include?('as:Limited') elsif audience_searchable_by.include?('kmyblue:Limited') || audience_searchable_by.include?('as:Limited')
:limited :limited
else else
:direct :direct

View file

@ -14,6 +14,7 @@ class EmojiReactService < BaseService
def call(account, status, name) def call(account, status, name)
status = status.reblog if status.reblog? && !status.reblog.nil? status = status.reblog if status.reblog? && !status.reblog.nil?
authorize_with account, status, :emoji_reaction? authorize_with account, status, :emoji_reaction?
@status = status
emoji_reaction = nil emoji_reaction = nil
@ -36,6 +37,9 @@ class EmojiReactService < BaseService
notify_to_followers(emoji_reaction) notify_to_followers(emoji_reaction)
bump_potential_friendship(account, status) bump_potential_friendship(account, status)
write_stream(emoji_reaction) write_stream(emoji_reaction)
forward_for_emoji_reaction!(emoji_reaction)
relay_for_emoji_reaction!(emoji_reaction)
relay_friend_for_emoji_reaction!(emoji_reaction)
emoji_reaction emoji_reaction
end end
@ -79,11 +83,33 @@ class EmojiReactService < BaseService
end end
def build_json(emoji_reaction) def build_json(emoji_reaction)
Oj.dump(serialize_payload(emoji_reaction, ActivityPub::EmojiReactionSerializer)) @build_json = Oj.dump(serialize_payload(emoji_reaction, ActivityPub::EmojiReactionSerializer, signer: emoji_reaction.account))
end end
def render_emoji_reaction(emoji_group) def render_emoji_reaction(emoji_group)
# @rendered_emoji_reaction ||= InlineRenderer.render(HashObject.new(emoji_group), nil, :emoji_reaction) # @rendered_emoji_reaction ||= InlineRenderer.render(HashObject.new(emoji_group), nil, :emoji_reaction)
@render_emoji_reaction ||= Oj.dump(event: :emoji_reaction, payload: emoji_group.to_json) @render_emoji_reaction ||= Oj.dump(event: :emoji_reaction, payload: emoji_group.to_json)
end end
def forward_for_emoji_reaction!(emoji_reaction)
return unless @status.local?
ActivityPub::RawDistributionWorker.perform_async(build_json(emoji_reaction), @status.account.id, [@status.account.preferred_inbox_url])
end
def relay_for_emoji_reaction!(emoji_reaction)
return unless @status.local? && @status.public_visibility?
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
[build_json(emoji_reaction), @status.account.id, inbox_url]
end
end
def relay_friend_for_emoji_reaction!(emoji_reaction)
return unless @status.local? && @status.distributable_friend?
ActivityPub::DeliveryWorker.push_bulk(FriendDomain.distributables.pluck(:inbox_url)) do |inbox_url|
[build_json(emoji_reaction), @status.account.id, inbox_url]
end
end
end end

View file

@ -57,7 +57,7 @@ class ReportService < BaseService
def forward_to_replied_to! def forward_to_replied_to!
# Send report to servers to which the account was replying to, so they also have a chance to act # Send report to servers to which the account was replying to, so they also have a chance to act
inbox_urls = Account.remote.where(domain: forward_to_domains).where(id: Status.where(id: reported_status_ids).where.not(in_reply_to_account_id: nil).select(:in_reply_to_account_id)).inboxes - [@target_account.inbox_url] inbox_urls = Account.remote.where(domain: forward_to_domains).where(id: Status.where(id: reported_status_ids).where.not(in_reply_to_account_id: nil).select(:in_reply_to_account_id)).inboxes - [@target_account.inbox_url, @target_account.shared_inbox_url]
inbox_urls.each do |inbox_url| inbox_urls.each do |inbox_url|
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)

View file

@ -15,6 +15,10 @@ class UnEmojiReactService < BaseService
create_notification(emoji_reaction) if !@status.account.local? && @status.account.activitypub? create_notification(emoji_reaction) if !@status.account.local? && @status.account.activitypub?
notify_to_followers(emoji_reaction) if @status.account.local? notify_to_followers(emoji_reaction) if @status.account.local?
write_stream(emoji_reaction) write_stream(emoji_reaction)
forward_for_undo_emoji_reaction!(emoji_reaction)
relay_for_undo_emoji_reaction!(emoji_reaction)
relay_friend_for_undo_emoji_reaction!(emoji_reaction)
else else
account = Account.find(account_id) account = Account.find(account_id)
bulk(account, @status) bulk(account, @status)
@ -52,11 +56,33 @@ class UnEmojiReactService < BaseService
end end
def build_json(emoji_reaction) def build_json(emoji_reaction)
Oj.dump(serialize_payload(emoji_reaction, ActivityPub::UndoEmojiReactionSerializer)) @build_json = Oj.dump(serialize_payload(emoji_reaction, ActivityPub::UndoEmojiReactionSerializer, signer: emoji_reaction.account))
end end
def render_emoji_reaction(emoji_group) def render_emoji_reaction(emoji_group)
# @rendered_emoji_reaction ||= InlineRenderer.render(emoji_group, nil, :emoji_reaction) # @rendered_emoji_reaction ||= InlineRenderer.render(emoji_group, nil, :emoji_reaction)
Oj.dump(event: :emoji_reaction, payload: emoji_group.to_json) Oj.dump(event: :emoji_reaction, payload: emoji_group.to_json)
end end
def forward_for_undo_emoji_reaction!(emoji_reaction)
return unless @status.local?
ActivityPub::RawDistributionWorker.perform_async(build_json(emoji_reaction), @status.account.id, [@status.account.preferred_inbox_url])
end
def relay_for_undo_emoji_reaction!(emoji_reaction)
return unless @status.local? && @status.public_visibility?
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
[build_json(emoji_reaction), @status.account.id, inbox_url]
end
end
def relay_friend_for_undo_emoji_reaction!(emoji_reaction)
return unless @status.local? && @status.distributable_friend?
ActivityPub::DeliveryWorker.push_bulk(FriendDomain.distributables.pluck(:inbox_url)) do |inbox_url|
[build_json(emoji_reaction), @status.account.id, inbox_url]
end
end
end end

View file

@ -7,7 +7,7 @@
= t 'admin.friend_servers.disabled' = t 'admin.friend_servers.disabled'
%samp= friend.domain %samp= friend.domain
%td %td
- if friend.i_am_accepted? - if friend.accepted?
%span.positive-hint %span.positive-hint
= fa_icon('check') = fa_icon('check')
= ' ' = ' '
@ -16,21 +16,11 @@
= fa_icon('hourglass') = fa_icon('hourglass')
= ' ' = ' '
= t 'admin.friend_servers.pending' = t 'admin.friend_servers.pending'
- else
%span.negative-hint
= fa_icon('times')
= ' '
= t 'admin.friend_servers.disabled'
%td
- if friend.they_are_accepted?
%span.positive-hint
= fa_icon('check')
= ' '
= t 'admin.friend_servers.enabled'
- elsif friend.they_are_pending? - elsif friend.they_are_pending?
= fa_icon('hourglass') %span.warning-hint
= ' ' = fa_icon('hourglass')
= t 'admin.friend_servers.pending' = ' '
= t 'admin.friend_servers.pending_you'
- else - else
%span.negative-hint %span.negative-hint
= fa_icon('times') = fa_icon('times')

View file

@ -10,11 +10,11 @@
.fields-group .fields-group
= f.input :available, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.available') = f.input :available, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.available')
.fields-group
= f.input :delivery_local, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.delivery_local'), hint: t('admin.friend_servers.edit.delivery_local_hint')
.fields-group .fields-group
= f.input :pseudo_relay, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.pseudo_relay'), hint: t('admin.friend_servers.edit.pseudo_relay_hint') = f.input :pseudo_relay, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.pseudo_relay'), hint: t('admin.friend_servers.edit.pseudo_relay_hint')
.fields-group
= f.input :unlocked, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.unlocked')
.fields-group .fields-group
= f.input :allow_all_posts, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.allow_all_posts'), hint: t('admin.friend_servers.edit.allow_all_posts_hint') = f.input :allow_all_posts, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.allow_all_posts'), hint: t('admin.friend_servers.edit.allow_all_posts_hint')

View file

@ -6,35 +6,14 @@
= render 'friend_fields', f: f, friend: @friend = render 'friend_fields', f: f, friend: @friend
.fields-group .fields-group
%h4= t('admin.friend_servers.active_status') %h4= t('admin.friend_servers.status')
.fields-group .fields-group
- if @friend.i_am_accepted? - if @friend.accepted?
%span.positive-hint %span.positive-hint
= fa_icon('check') = fa_icon('check')
= ' ' = ' '
= t 'admin.friend_servers.enabled' = t 'admin.friend_servers.enabled'
- elsif @friend.i_am_pending? - elsif @friend.pending?
= fa_icon('hourglass')
= ' '
= t 'admin.friend_servers.pending'
- else
%span.negative-hint
= fa_icon('times')
= ' '
= t 'admin.friend_servers.disabled'
.action-buttons
%div
= link_to t('admin.friend_servers.follow'), follow_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.i_am_idle? || @friend.i_am_rejected?
= link_to t('admin.friend_servers.unfollow'), unfollow_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.i_am_pending? || @friend.i_am_accepted?
%h4= t('admin.friend_servers.passive_status')
.fields-gtoup
- if @friend.they_are_accepted?
%span.positive-hint
= fa_icon('check')
= ' '
= t 'admin.friend_servers.enabled'
- elsif @friend.they_are_pending?
= fa_icon('hourglass') = fa_icon('hourglass')
= ' ' = ' '
= t 'admin.friend_servers.pending' = t 'admin.friend_servers.pending'
@ -45,6 +24,7 @@
= t 'admin.friend_servers.disabled' = t 'admin.friend_servers.disabled'
.action-buttons .action-buttons
%div %div
= link_to t('admin.friend_servers.follow'), follow_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.idle?
= link_to t('admin.friend_servers.accept'), accept_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.they_are_pending? = link_to t('admin.friend_servers.accept'), accept_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.they_are_pending?
= link_to t('admin.friend_servers.reject'), reject_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.they_are_pending? = link_to t('admin.friend_servers.reject'), reject_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.they_are_pending?

View file

@ -13,8 +13,7 @@
%thead %thead
%tr %tr
%th= t('admin.friend_servers.domain') %th= t('admin.friend_servers.domain')
%th= t('admin.friend_servers.active_status') %th= t('admin.friend_servers.status')
%th= t('admin.friend_servers.passive_status')
%th %th
%tbody %tbody
- @friends.each do |friend| - @friends.each do |friend|

View file

@ -85,7 +85,7 @@ si:
signed_up_but_locked: ඔබ සාර්ථකව ලියාපදිංචි වී ඇත. කෙසේ වෙතත්, ඔබගේ ගිණුම අගුලු දමා ඇති නිසා අපට ඔබව පුරනය කිරීමට නොහැකි විය. signed_up_but_locked: ඔබ සාර්ථකව ලියාපදිංචි වී ඇත. කෙසේ වෙතත්, ඔබගේ ගිණුම අගුලු දමා ඇති නිසා අපට ඔබව පුරනය කිරීමට නොහැකි විය.
signed_up_but_pending: තහවුරු කිරීමේ සබැඳියක් සහිත පණිවිඩයක් ඔබගේ විද්‍යුත් තැපැල් ලිපිනයට යවා ඇත. ඔබ සබැඳිය ක්ලික් කළ පසු, අපි ඔබගේ අයදුම්පත සමාලෝචනය කරන්නෙමු. එය අනුමත වුවහොත් ඔබට දැනුම් දෙනු ලැබේ. signed_up_but_pending: තහවුරු කිරීමේ සබැඳියක් සහිත පණිවිඩයක් ඔබගේ විද්‍යුත් තැපැල් ලිපිනයට යවා ඇත. ඔබ සබැඳිය ක්ලික් කළ පසු, අපි ඔබගේ අයදුම්පත සමාලෝචනය කරන්නෙමු. එය අනුමත වුවහොත් ඔබට දැනුම් දෙනු ලැබේ.
signed_up_but_unconfirmed: තහවුරු කිරීමේ සබැඳියක් සහිත පණිවිඩයක් ඔබගේ විද්‍යුත් තැපැල් ලිපිනයට යවා ඇත. ඔබගේ ගිණුම සක්‍රිය කිරීමට කරුණාකර සබැඳිය අනුගමනය කරන්න. ඔබට මෙම විද්‍යුත් තැපෑල නොලැබුනේ නම් කරුණාකර ඔබගේ අයාචිත තැපැල් ෆෝල්ඩරය පරීක්ෂා කරන්න. signed_up_but_unconfirmed: තහවුරු කිරීමේ සබැඳියක් සහිත පණිවිඩයක් ඔබගේ විද්‍යුත් තැපැල් ලිපිනයට යවා ඇත. ඔබගේ ගිණුම සක්‍රිය කිරීමට කරුණාකර සබැඳිය අනුගමනය කරන්න. ඔබට මෙම විද්‍යුත් තැපෑල නොලැබුනේ නම් කරුණාකර ඔබගේ අයාචිත තැපැල් ෆෝල්ඩරය පරීක්ෂා කරන්න.
update_needs_confirmation: ඔබගේ ගිණුම සාර්ථකව යාවත්කාලීන වුවද අපට නව වි-තැපැල් ලිපිනය තහවුරු කර ගැනීමට වුවමනා කෙරේ. කරුණාකර ඔබගේ වි-තැපෑල පරීක්‍ෂා කර ඊට අදාළ සබැඳිය අනුගමනය කර ඔබගේ නව වි-තැපැල් ලිපිනය තහවුරු කරන්න. ඔබට මෙම වි-තැපෑල නොලැබුණේ නම් කරුණාකර අයාචිත තැපැල් බහාලුම බලන්න. update_needs_confirmation: ඔබගේ ගිණුම සාර්ථකව යාවත්කාලීන වුවද අපට නව වි-තැපැල් ලිපිනය සත්‍යාපනය කර ගැනීමට වුවමනා කෙරේ. කරුණාකර ඔබගේ වි-තැපෑල පරීක්‍ෂා කර ඊට අදාළ සබැඳිය අනුගමනය කර ඔබගේ නව වි-තැපැල් ලිපිනය තහවුරු කරන්න. ඔබට මෙම වි-තැපෑල නොලැබුණේ නම් කරුණාකර අයාචිත තැපැල් බහාලුම බලන්න.
updated: ඔබගේ ගිණුම සාර්ථකව යාවත්කාලීන කර ඇත. updated: ඔබගේ ගිණුම සාර්ථකව යාවත්කාලීන කර ඇත.
sessions: sessions:
already_signed_out: සාර්ථකව නික්මිණි. already_signed_out: සාර්ථකව නික්මිණි.

View file

@ -501,7 +501,6 @@ en:
unsuppress: Restore follow recommendation unsuppress: Restore follow recommendation
friend_servers: friend_servers:
accept: Accept accept: Accept
active_status: My status
add_new: Add and make a new application add_new: Add and make a new application
delete: Delete delete: Delete
description_html: <strong>フレンドサーバー</strong>とは、お互いのローカル公開・ローカル検索許可の投稿をそのまま交換するシステムです。 description_html: <strong>フレンドサーバー</strong>とは、お互いのローカル公開・ローカル検索許可の投稿をそのまま交換するシステムです。
@ -511,6 +510,8 @@ en:
allow_all_posts: Receive all posts allow_all_posts: Receive all posts
allow_all_posts_hint: 通常は自分のサーバーの誰もフォローしていないアカウントの投稿は例外を除き受け入れがブロックされます。そのブロックを解除します。スパムが発生した場合など、いつでもブロックを再開できます。 allow_all_posts_hint: 通常は自分のサーバーの誰もフォローしていないアカウントの投稿は例外を除き受け入れがブロックされます。そのブロックを解除します。スパムが発生した場合など、いつでもブロックを再開できます。
available: Available available: Available
delivery_local: Deliver without changing public unlisted visibility and searchability
delivery_local_hint: Public unlisted posts will be added the friend's global timeline
description: フレンドサーバーは、登録と同時に相手方のサーバーへ申請されます。 description: フレンドサーバーは、登録と同時に相手方のサーバーへ申請されます。
domain: Domain domain: Domain
inbox_url: Friend server inbox URL inbox_url: Friend server inbox URL
@ -521,8 +522,8 @@ en:
edit_friend: Edit edit_friend: Edit
enabled: Enabled enabled: Enabled
follow: Request follow: Request
passive_status: Partner status
pending: Pending pending: Pending
pending_you: Your review requested
reject: Reject reject: Reject
save_and_enable: Save and enable save_and_enable: Save and enable
setup: Add and make a new application setup: Add and make a new application

View file

@ -496,7 +496,6 @@ ja:
unsuppress: おすすめフォローを復元 unsuppress: おすすめフォローを復元
friend_servers: friend_servers:
accept: 相手の申請を承認する accept: 相手の申請を承認する
active_status: 自分の状態
add_new: フレンドサーバーを追加・申請 add_new: フレンドサーバーを追加・申請
delete: 削除 delete: 削除
description_html: <strong>フレンドサーバー</strong>とは、お互いのローカル公開・ローカル検索許可の投稿をそのまま交換するシステムです。 description_html: <strong>フレンドサーバー</strong>とは、お互いのローカル公開・ローカル検索許可の投稿をそのまま交換するシステムです。
@ -506,6 +505,8 @@ ja:
allow_all_posts: このサーバーからの投稿を無条件で受け入れる allow_all_posts: このサーバーからの投稿を無条件で受け入れる
allow_all_posts_hint: 通常は自分のサーバーの誰もフォローしていないアカウントの投稿は例外を除き受け入れがブロックされます。そのブロックを解除します。スパムが発生した場合など、いつでもブロックを再開できます。 allow_all_posts_hint: 通常は自分のサーバーの誰もフォローしていないアカウントの投稿は例外を除き受け入れがブロックされます。そのブロックを解除します。スパムが発生した場合など、いつでもブロックを再開できます。
available: 有効にする available: 有効にする
delivery_local: ローカル公開の公開範囲・検索許可を持った投稿をそのまま相手と共有する
delivery_local_hint: ローカル公開投稿は、通常は非収載に変換されて配送されます。その処理をせず、相手サーバーにもローカル公開と認識されるようにします。相手の連合タイムラインに掲載されます
description: フレンドサーバーは、登録と同時に相手方のサーバーへ申請されます。 description: フレンドサーバーは、登録と同時に相手方のサーバーへ申請されます。
domain: ドメイン domain: ドメイン
inbox_url: フレンドサーバーの inbox URL inbox_url: フレンドサーバーの inbox URL
@ -516,8 +517,8 @@ ja:
edit_friend: 編集 edit_friend: 編集
enabled: 有効 enabled: 有効
follow: こちらから申請する follow: こちらから申請する
passive_status: 相手の状態
pending: 承認待ち pending: 承認待ち
pending_you: あなたの承認が必要
reject: 相手からの申請を却下する reject: 相手からの申請を却下する
save_and_enable: 保存して有効にする save_and_enable: 保存して有効にする
setup: フレンドサーバーを追加・申請 setup: フレンドサーバーを追加・申請

View file

@ -10,7 +10,7 @@ si:
followers: followers:
one: අනුගාමිකයා one: අනුගාමිකයා
other: අනුගාමිකයින් other: අනුගාමිකයින්
following: අනුගමන following: අනුගමන
instance_actor_flash: මෙම ගිණුම සේවාදායකයම නියෝජනය කිරීමට භාවිතා කරන අතථ්‍ය නළුවෙකු වන අතර කිසිදු තනි පරිශීලකයෙකු නොවේ. එය ෆෙඩරේෂන් අරමුණු සඳහා භාවිතා කරන අතර අත්හිටුවිය යුතු නොවේ. instance_actor_flash: මෙම ගිණුම සේවාදායකයම නියෝජනය කිරීමට භාවිතා කරන අතථ්‍ය නළුවෙකු වන අතර කිසිදු තනි පරිශීලකයෙකු නොවේ. එය ෆෙඩරේෂන් අරමුණු සඳහා භාවිතා කරන අතර අත්හිටුවිය යුතු නොවේ.
last_active: අවසාන ක්රියාකාරී last_active: අවසාන ක්රියාකාරී
link_verified_on: මෙම සබැඳියේ හිමිකාරිත්වය %{date}හි පරීක්ෂා කරන ලදී link_verified_on: මෙම සබැඳියේ හිමිකාරිත්වය %{date}හි පරීක්ෂා කරන ලදී
@ -382,7 +382,7 @@ si:
status: තත්‍වය status: තත්‍වය
suppress: අනුගමනය නිර්දේශය යටපත් කරන්න suppress: අනුගමනය නිර්දේශය යටපත් කරන්න
suppressed: යටපත් කළා suppressed: යටපත් කළා
title: නිර්දේශ අනුගමනය කරන්න title: අනුගමනයට නිර්දේශ
unsuppress: නිර්දේශ පිළිපැදීම ප්‍රතිසාධනය කරන්න unsuppress: නිර්දේශ පිළිපැදීම ප්‍රතිසාධනය කරන්න
instances: instances:
availability: availability:
@ -621,7 +621,7 @@ si:
history: අනුවාද ඉතිහාසය history: අනුවාද ඉතිහාසය
language: භාෂාව language: භාෂාව
media: media:
title: මාධ්‍ය title: මාධ්‍ය
metadata: පාරදත්ත metadata: පාරදත්ත
no_status_selected: කිසිවක් නොතේරූ බැවින් ලිපි කිසිවක් වෙනස් කර නැත no_status_selected: කිසිවක් නොතේරූ බැවින් ලිපි කිසිවක් වෙනස් කර නැත
open: ලිපිය අරින්න open: ලිපිය අරින්න
@ -787,7 +787,7 @@ si:
localization: localization:
body: මාස්ටඩන් ස්වේච්ඡාවෙන් පරිවර්තනය කර ඇත. body: මාස්ටඩන් ස්වේච්ඡාවෙන් පරිවර්තනය කර ඇත.
guide_link: https://crowdin.com/project/mastodon guide_link: https://crowdin.com/project/mastodon
guide_link_text: සෑම කෙනෙකුටම දායක විය හැකිය. guide_link_text: පරිවර්තකයින්ට දායක වීමට හැකිය.
sensitive_content: සංවේදී අන්තර්ගත sensitive_content: සංවේදී අන්තර්ගත
application_mailer: application_mailer:
notification_preferences: ඊමේල් මනාප වෙනස් කරන්න notification_preferences: ඊමේල් මනාප වෙනස් කරන්න
@ -1191,7 +1191,7 @@ si:
relationships: relationships:
activity: ගිණුමේ ක්‍රියාකාරකම් activity: ගිණුමේ ක්‍රියාකාරකම්
dormant: නිදිමතයි dormant: නිදිමතයි
follow_selected_followers: ෝරාගත් අනුගාමිකයින් අනුගමනය කරන්න follow_selected_followers: ේරූ අනුගාමිකයින් අනුගමනය කරන්න
followers: අනුගාමිකයින් followers: අනුගාමිකයින්
following: අනුගමනය following: අනුගමනය
invited: ආරාධනා කළා invited: ආරාධනා කළා
@ -1201,9 +1201,9 @@ si:
mutual: අන්යෝන්ය mutual: අන්යෝන්ය
primary: ප්‍රාථමික primary: ප්‍රාථමික
relationship: සම්බන්ධතාවය relationship: සම්බන්ධතාවය
remove_selected_domains: ෝරාගත් වසම් වලින් සියලුම අනුගාමිකයින් ඉවත් කරන්න remove_selected_domains: ේරූ වසම් වල සියලුම අනුගාමිකයින් ඉවත් කරන්න
remove_selected_followers: ෝරාගත් අනුගාමිකයින් ඉවත් කරන්න remove_selected_followers: ේරූ අනුගාමිකයින් ඉවත් කරන්න
remove_selected_follows: ෝරාගත් පරිශීලකයින් අනුගමනය නොකරන්න remove_selected_follows: ේරූ අය අනුගමනය නොකරන්න
status: ගිණුමේ තත්‍වය status: ගිණුමේ තත්‍වය
remote_follow: remote_follow:
missing_resource: ඔබගේ ගිණුම සඳහා අවශ්‍ය යළි-යොමුවීම් URL එක සොයා ගැනීමට නොහැකි විය missing_resource: ඔබගේ ගිණුම සඳහා අවශ්‍ය යළි-යොමුවීම් URL එක සොයා ගැනීමට නොහැකි විය
@ -1279,7 +1279,7 @@ si:
notifications: දැනුම්දීම් notifications: දැනුම්දීම්
preferences: අභිප්‍රේත preferences: අභිප්‍රේත
profile: ප්‍රසිද්ධ පැතිකඩ profile: ප්‍රසිද්ධ පැතිකඩ
relationships: අනුගමිකයින් සහ අනුගාමිකයින් relationships: අනුගමන හා අනුගාමික
statuses_cleanup: ස්වයංක්‍රීය ලිපි මැකීම statuses_cleanup: ස්වයංක්‍රීය ලිපි මැකීම
two_factor_authentication: ද්වි සාධක Aut two_factor_authentication: ද්වි සාධක Aut
webauthn_authentication: ආරක්‍ෂණ යතුරු webauthn_authentication: ආරක්‍ෂණ යතුරු
@ -1324,7 +1324,7 @@ si:
title: '%{name}: "%{quote}"' title: '%{name}: "%{quote}"'
visibilities: visibilities:
direct: සෘජු direct: සෘජු
private: අනුගාමිකයින්-පමණි private: අනුගාමිකයින් පමණි
private_long: අනුගාමිකයින්ට පමණක් පෙන්වන්න private_long: අනුගාමිකයින්ට පමණක් පෙන්වන්න
public: ප්‍රසිද්ධ public: ප්‍රසිද්ධ
public_long: හැමෝටම පේනවා public_long: හැමෝටම පේනවා
@ -1336,12 +1336,13 @@ si:
ignore_favs: ප්‍රියතමයන් නොසලකන්න ignore_favs: ප්‍රියතමයන් නොසලකන්න
interaction_exceptions: අන්තර්ක්‍රියා මත පදනම් වූ ව්‍යතිරේක interaction_exceptions: අන්තර්ක්‍රියා මත පදනම් වූ ව්‍යතිරේක
keep_direct: සෘජු පණිවිඩ තබාගන්න keep_direct: සෘජු පණිවිඩ තබාගන්න
keep_direct_hint: ඔබගේ සෘජු පණිවිඩ කිසිවක් මැකෙන්නේ නැත keep_direct_hint: ඔබගේ සෘජු පණිවිඩ කිසිවක් නොමැකෙයි
keep_media: මාධ්‍ය ඇමුණුම් සහිත ලිපි තබාගන්න keep_media: මාධ්‍ය ඇමුණුම් සහිත ලිපි තබාගන්න
keep_media_hint: මාධ්‍ය ඇමුණුම් සහිත ඔබගේ ලිපි කිසිවක් මැකෙන්නේ නැත keep_media_hint: මාධ්‍ය ඇමුණුම් සහිත ඔබගේ ලිපි කිසිවක් නොමැකෙයි
keep_pinned: ඇමිණූ ලිපි තබාගන්න keep_pinned: ඇමිණූ ලිපි තබාගන්න
keep_pinned_hint: ඔබ ඇමිණූ ලිපි කිසිවක් නොමැකෙයි keep_pinned_hint: ඔබ ඇමිණූ ලිපි කිසිවක් නොමැකෙයි
keep_polls_hint: ඔබගේ මත විමසුම් මැකෙන්නේ නැත keep_polls: මත විමසුම් තබාගන්න
keep_polls_hint: ඔබගේ මත විමසුම් නොමැකෙයි
keep_self_bookmark: ඔබ පොත්යොමු තැබූ ලිපි තබාගන්න keep_self_bookmark: ඔබ පොත්යොමු තැබූ ලිපි තබාගන්න
keep_self_bookmark_hint: ඔබගේම ලිපි වලට පොත්යොමු තබා ඇත්නම් ඒවා මකා නොදැමෙයි keep_self_bookmark_hint: ඔබගේම ලිපි වලට පොත්යොමු තබා ඇත්නම් ඒවා මකා නොදැමෙයි
keep_self_fav: ඔබ ප්‍රිය කළ ලිපි තබාගන්න keep_self_fav: ඔබ ප්‍රිය කළ ලිපි තබාගන්න
@ -1355,7 +1356,7 @@ si:
'604800': සති 1 '604800': සති 1
'63113904': අවුරුදු 2 '63113904': අවුරුදු 2
'7889238': මාස 3 '7889238': මාස 3
min_age_label: වයස් සීමාව min_age_label: කාල සීමාව
min_favs: අවම වශයෙන් ප්‍රිය කළ ලිපි තබාගන්න min_favs: අවම වශයෙන් ප්‍රිය කළ ලිපි තබාගන්න
stream_entries: stream_entries:
sensitive_content: සංවේදී අන්තර්ගතයකි sensitive_content: සංවේදී අන්තර්ගතයකි

View file

@ -322,7 +322,7 @@ es-MX:
url: URL de Endpoint url: URL de Endpoint
'no': 'No' 'no': 'No'
not_recommended: No recomendado not_recommended: No recomendado
overridden: Sobrescrito overridden: Reemplazado
recommended: Recomendado recommended: Recomendado
required: required:
mark: "*" mark: "*"

View file

@ -322,6 +322,7 @@ my:
url: URL ဆုံးမှတ် url: URL ဆုံးမှတ်
'no': 'မလုပ်ပါ' 'no': 'မလုပ်ပါ'
not_recommended: ထောက်ခံထားမှုမရှိ not_recommended: ထောက်ခံထားမှုမရှိ
overridden: ပယ်ဖျက်
recommended: ထောက်ခံထားပြီး recommended: ထောက်ခံထားပြီး
required: required:
mark: "*" mark: "*"

View file

@ -89,6 +89,8 @@ si:
fields: fields:
name: නම්පත name: නම්පත
value: අන්තර්ගතය value: අන්තර්ගතය
show_collections: අනුගමන හා අනුගාමිකයින් පැතිකඩෙහි පෙන්වන්න
unlocked: නව අනුගාමිකයින් ස්වයංක්‍රීයව පිළිගන්න
account_alias: account_alias:
acct: පැරණි ගිණුමේ හැසිරවීම acct: පැරණි ගිණුමේ හැසිරවීම
account_migration: account_migration:
@ -117,8 +119,9 @@ si:
appeal: appeal:
text: මෙම තීරණය ආපසු හැරවිය යුත්තේ මන්දැයි පැහැදිලි කරන්න text: මෙම තීරණය ආපසු හැරවිය යුත්තේ මන්දැයි පැහැදිලි කරන්න
defaults: defaults:
autofollow: ඔබගේ ගිණුම අනුගමනය කිරීමට ආරාධනා කරන්න autofollow: ඔබගේ ගිණුම අනුගමනයට ආරාධනා කරන්න
avatar: පැතිකඩ ඡායාරූපය avatar: පැතිකඩ ඡායාරූපය
bot: මෙම ගිණුම ස්වයංක්‍රියයි
chosen_languages: භාෂා පෙරන්න chosen_languages: භාෂා පෙරන්න
confirm_new_password: නව මුරපදය තහවුරු කරන්න confirm_new_password: නව මුරපදය තහවුරු කරන්න
confirm_password: මුරපදය තහවුරු කරන්න confirm_password: මුරපදය තහවුරු කරන්න
@ -139,7 +142,7 @@ si:
otp_attempt: ද්වි සාධක කේතය otp_attempt: ද්වි සාධක කේතය
password: මුරපදය password: මුරපදය
phrase: මූල පදය හෝ වාක්‍ය ඛණ්ඩය phrase: මූල පදය හෝ වාක්‍ය ඛණ්ඩය
setting_advanced_layout: උසස් වෙබ් අතුරු මුහුණත සබල කරන්න setting_advanced_layout: සංකීර්ණ අතුරු මුහුණත සබල කරන්න
setting_always_send_emails: සෑම විටම විද්‍යුත් තැපැල් දැනුම්දීම් යවන්න setting_always_send_emails: සෑම විටම විද්‍යුත් තැපැල් දැනුම්දීම් යවන්න
setting_auto_play_gif: සජීවිකරණ GIF ස්වයංක්‍රීයව ධාවනය කරන්න setting_auto_play_gif: සජීවිකරණ GIF ස්වයංක්‍රීයව ධාවනය කරන්න
setting_default_language: ලිපිවල භාෂාව setting_default_language: ලිපිවල භාෂාව

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
require Rails.root.join('lib', 'mastodon', 'migration_helpers')
class AddDeliveryLocalToFriendDomains < ActiveRecord::Migration[7.0]
include Mastodon::MigrationHelpers
disable_ddl_transaction!
def up
safety_assured do
add_column_with_default :friend_domains, :delivery_local, :boolean, default: true, allow_null: false
remove_column :friend_domains, :unlocked
end
end
def down
safety_assured do
remove_column :friend_domains, :delivery_local
add_column_with_default :friend_domains, :unlocked, :boolean, default: false, allow_null: false
end
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_10_07_090808) do ActiveRecord::Schema[7.0].define(version: 2023_10_09_235215) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -686,10 +686,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_07_090808) do
t.string "passive_follow_activity_id" t.string "passive_follow_activity_id"
t.boolean "available", default: true, null: false t.boolean "available", default: true, null: false
t.boolean "pseudo_relay", default: false, null: false t.boolean "pseudo_relay", default: false, null: false
t.boolean "unlocked", default: false, null: false
t.boolean "allow_all_posts", default: true, null: false t.boolean "allow_all_posts", default: true, null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.boolean "delivery_local", default: true, null: false
t.index ["domain"], name: "index_friend_domains_on_domain", unique: true t.index ["domain"], name: "index_friend_domains_on_domain", unique: true
t.index ["inbox_url"], name: "index_friend_domains_on_inbox_url", unique: true t.index ["inbox_url"], name: "index_friend_domains_on_inbox_url", unique: true
end end

View file

@ -9,7 +9,7 @@ module Mastodon
end end
def kmyblue_minor def kmyblue_minor
1 0
end end
def kmyblue_flag def kmyblue_flag
@ -25,7 +25,7 @@ module Mastodon
end end
def patch def patch
0 1
end end
def default_prerelease def default_prerelease

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
Fabricator(:emoji_reaction) do
account { Fabricate.build(:account) }
status { Fabricate.build(:status) }
name '😀'
end

View file

@ -118,5 +118,26 @@ RSpec.describe ActivityPub::Activity::Accept do
subject.perform subject.perform
expect(friend.reload.i_am_accepted?).to be true expect(friend.reload.i_am_accepted?).to be true
end end
it 'when the friend server is pending' do
friend.update(passive_state: :pending)
subject.perform
expect(friend.reload.they_are_idle?).to be true
expect(friend.i_am_accepted?).to be true
end
it 'when the friend server is accepted' do
friend.update(passive_state: :accepted)
subject.perform
expect(friend.reload.they_are_idle?).to be true
expect(friend.i_am_accepted?).to be true
end
it 'when my server is not pending' do
friend.update(active_state: :idle)
subject.perform
expect(friend.reload.i_am_idle?).to be true
expect(friend.they_are_idle?).to be true
end
end end
end end

View file

@ -30,9 +30,11 @@ RSpec.describe ActivityPub::Activity::Create do
let(:sender_software) { 'mastodon' } let(:sender_software) { 'mastodon' }
let(:custom_before) { false } let(:custom_before) { false }
let(:active_friend) { false }
before do before do
Fabricate(:instance_info, domain: 'example.com', software: sender_software) Fabricate(:instance_info, domain: 'example.com', software: sender_software)
Fabricate(:friend_domain, domain: 'example.com', active_state: :accepted) if active_friend
subject.perform unless custom_before subject.perform unless custom_before
end end
@ -234,17 +236,37 @@ RSpec.describe ActivityPub::Activity::Create do
end end
end end
context 'when public_unlisted with LocalPublic' do context 'when public_unlisted with kmyblue:LocalPublic' do
let(:object_json) do let(:object_json) do
{ {
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note', type: 'Note',
content: 'Lorem ipsum', content: 'Lorem ipsum',
to: ['http://example.com/followers', 'LocalPublic'], to: ['http://example.com/followers', 'kmyblue:LocalPublic'],
cc: 'https://www.w3.org/ns/activitystreams#Public', cc: 'https://www.w3.org/ns/activitystreams#Public',
} }
end end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'unlisted'
end
end
context 'when public_unlisted with kmyblue:LocalPublic from friend-server' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: ['http://example.com/followers', 'kmyblue:LocalPublic'],
cc: 'https://www.w3.org/ns/activitystreams#Public',
}
end
let(:active_friend) { true }
it 'creates status' do it 'creates status' do
status = sender.statuses.first status = sender.statuses.first
@ -430,8 +452,20 @@ RSpec.describe ActivityPub::Activity::Create do
end end
end end
context 'with public_unlisted with LocalPublic' do context 'with public_unlisted with kmyblue:LocalPublic' do
let(:searchable_by) { ['http://example.com/followers', 'LocalPublic'] } let(:searchable_by) { ['http://example.com/followers', 'kmyblue:LocalPublic'] }
it 'create status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.searchability).to eq 'private'
end
end
context 'with public_unlisted with kmyblue:LocalPublic from friend-server' do
let(:searchable_by) { ['http://example.com/followers', 'kmyblue:LocalPublic'] }
let(:active_friend) { true }
it 'create status' do it 'create status' do
status = sender.statuses.first status = sender.statuses.first
@ -475,6 +509,17 @@ RSpec.describe ActivityPub::Activity::Create do
end end
context 'with limited' do context 'with limited' do
let(:searchable_by) { 'kmyblue:Limited' }
it 'create status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.searchability).to eq 'limited'
end
end
context 'with limited old spec' do
let(:searchable_by) { 'as:Limited' } let(:searchable_by) { 'as:Limited' }
it 'create status' do it 'create status' do
@ -600,7 +645,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with misskey' do context 'with misskey' do
let(:sender_software) { 'misskey' } let(:sender_software) { 'misskey' }
let(:searchable_by) { 'as:Limited' } let(:searchable_by) { 'kmyblue:Limited' }
it 'create status' do it 'create status' do
status = sender.statuses.first status = sender.statuses.first
@ -1495,11 +1540,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when sender is in friend server' do context 'when sender is in friend server' do
subject { described_class.new(json, sender, delivery: true) } subject { described_class.new(json, sender, delivery: true) }
before do let!(:friend) { Fabricate(:friend_domain, domain: sender.domain, active_state: :accepted) }
Fabricate(:friend_domain, domain: sender.domain, active_state: :accepted, passive_state: :accepted)
subject.perform
end
let(:object_json) do let(:object_json) do
{ {
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@ -1509,11 +1550,20 @@ RSpec.describe ActivityPub::Activity::Create do
end end
it 'creates status' do it 'creates status' do
subject.perform
status = sender.statuses.first status = sender.statuses.first
expect(status).to_not be_nil expect(status).to_not be_nil
expect(status.text).to eq 'Lorem ipsum' expect(status.text).to eq 'Lorem ipsum'
end end
it 'whey no-relay not creates status' do
friend.update(allow_all_posts: false)
subject.perform
status = sender.statuses.first
expect(status).to be_nil
end
end end
context 'when the sender has no relevance to local activity' do context 'when the sender has no relevance to local activity' do

View file

@ -339,6 +339,37 @@ RSpec.describe ActivityPub::Activity::Follow do
expect(friend).to_not be_nil expect(friend).to_not be_nil
expect(friend.they_are_pending?).to be true expect(friend.they_are_pending?).to be true
expect(friend.passive_follow_activity_id).to eq 'foo' expect(friend.passive_follow_activity_id).to eq 'foo'
expect(friend.inbox_url).to eq 'https://abc.com/inbox'
end
end
context 'when my server is pending' do
before do
friend.update(active_state: :pending)
end
it 'marks me as idle' do
subject.perform
expect(friend.reload.they_are_pending?).to be true
expect(friend.i_am_idle?).to be true
end
end
context 'when my server is already accepted' do
before do
friend.update(active_state: :accepted)
stub_request(:post, 'https://example.com/inbox')
end
it 'marks me as idle and the friend as accepted' do
subject.perform
expect(friend.reload.they_are_accepted?).to be true
expect(friend.i_am_idle?).to be true
expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({
id: 'foo#accepts/friends',
type: 'Accept',
object: 'foo',
}))).to have_been_made.once
end end
end end
@ -372,26 +403,6 @@ RSpec.describe ActivityPub::Activity::Follow do
end end
end end
context 'when unlocked' do
before do
friend.update(unlocked: true)
stub_request(:post, 'https://example.com/inbox')
end
it 'marks the friend as accepted' do
subject.perform
friend = FriendDomain.find_by(domain: 'abc.com')
expect(friend).to_not be_nil
expect(friend.they_are_accepted?).to be true
expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({
id: 'foo#accepts/friends',
type: 'Accept',
object: 'foo',
}))).to have_been_made.once
end
end
context 'when unlocked on admin settings' do context 'when unlocked on admin settings' do
before do before do
Form::AdminSettings.new(unlocked_friend: '1').save Form::AdminSettings.new(unlocked_friend: '1').save

View file

@ -192,5 +192,19 @@ RSpec.describe ActivityPub::Activity::Reject do
subject.perform subject.perform
expect(friend.reload.i_am_rejected?).to be true expect(friend.reload.i_am_rejected?).to be true
end end
it 'when the friend server is pending' do
friend.update(passive_state: :pending)
subject.perform
expect(friend.reload.they_are_idle?).to be true
expect(friend.i_am_rejected?).to be true
end
it 'when the friend server is accepted' do
friend.update(passive_state: :accepted)
subject.perform
expect(friend.reload.they_are_idle?).to be true
expect(friend.i_am_rejected?).to be true
end
end end
end end

View file

@ -149,7 +149,7 @@ RSpec.describe ActivityPub::Activity::Undo do
friend = Fabricate(:friend_domain, domain: sender.domain, passive_state: :accepted) friend = Fabricate(:friend_domain, domain: sender.domain, passive_state: :accepted)
subject.perform subject.perform
expect(sender.following?(recipient)).to be false expect(sender.following?(recipient)).to be false
expect(friend.they_are_accepted?).to be true expect(friend.reload.they_are_accepted?).to be true
end end
context 'with only object uri' do context 'with only object uri' do
@ -175,8 +175,19 @@ RSpec.describe ActivityPub::Activity::Undo do
it 'deletes follow from this server to friend' do it 'deletes follow from this server to friend' do
subject.perform subject.perform
expect(friend.reload.they_are_idle?).to be true expect(FriendDomain.exists?(domain: 'abc.com')).to be false
expect(friend.passive_follow_activity_id).to be_nil end
it 'when my server is pending' do
friend.update(active_state: :pending)
subject.perform
expect(FriendDomain.exists?(domain: 'abc.com')).to be false
end
it 'when my server is accepted' do
friend.update(active_state: :accepted)
subject.perform
expect(FriendDomain.exists?(domain: 'abc.com')).to be false
end end
end end
end end
@ -202,5 +213,32 @@ RSpec.describe ActivityPub::Activity::Undo do
expect(sender.favourited?(status)).to be false expect(sender.favourited?(status)).to be false
end end
end end
context 'with EmojiReact' do
let(:status) { Fabricate(:status) }
let(:content) { '😀' }
let(:object_json) do
{
id: 'bar',
type: 'Like',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: ActivityPub::TagManager.instance.uri_for(status),
content: content,
}
end
before do
Fabricate(:favourite, account: sender, status: status)
Fabricate(:emoji_reaction, account: sender, status: status, name: content)
end
it 'delete emoji reaction' do
subject.perform
reaction = EmojiReaction.find_by(account: sender, status: status)
expect(reaction).to be_nil
expect(sender.favourited?(status)).to be true
end
end
end end
end end

View file

@ -77,7 +77,7 @@ RSpec.describe ActivityPub::TagManager do
describe '#to_for_friend' do describe '#to_for_friend' do
it 'returns followers collection for public_unlisted status' do it 'returns followers collection for public_unlisted status' do
status = Fabricate(:status, visibility: :public_unlisted) status = Fabricate(:status, visibility: :public_unlisted)
expect(subject.to_for_friend(status)).to eq [account_followers_url(status.account), 'LocalPublic'] expect(subject.to_for_friend(status)).to eq [account_followers_url(status.account), 'kmyblue:LocalPublic']
end end
it 'returns followers collection for unlisted status' do it 'returns followers collection for unlisted status' do
@ -188,7 +188,7 @@ RSpec.describe ActivityPub::TagManager do
it 'returns as:Limited array for limited status' do it 'returns as:Limited array for limited status' do
status = Fabricate(:status, searchability: :limited) status = Fabricate(:status, searchability: :limited)
expect(subject.searchable_by(status)).to eq ['as:Limited'] expect(subject.searchable_by(status)).to eq ['as:Limited', 'kmyblue:Limited']
end end
end end
@ -200,7 +200,7 @@ RSpec.describe ActivityPub::TagManager do
it 'returns public collection for public_unlisted status' do it 'returns public collection for public_unlisted status' do
status = Fabricate(:status, account: Fabricate(:account, searchability: :public), searchability: :public_unlisted) status = Fabricate(:status, account: Fabricate(:account, searchability: :public), searchability: :public_unlisted)
expect(subject.searchable_by_for_friend(status)).to eq [account_followers_url(status.account), 'LocalPublic'] expect(subject.searchable_by_for_friend(status)).to eq [account_followers_url(status.account), 'kmyblue:LocalPublic']
end end
it 'returns followers collection for private status' do it 'returns followers collection for private status' do

View file

@ -99,7 +99,7 @@ describe StatusReachFinder do
let(:sender_software) { 'misskey' } let(:sender_software) { 'misskey' }
let(:searchability) { :public } let(:searchability) { :public }
before { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', available: true, active_state: :accepted, passive_state: :accepted, pseudo_relay: true) } before { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', available: true, active_state: :accepted, pseudo_relay: true) }
it 'send status without friend server' do it 'send status without friend server' do
expect(subject.inboxes).to_not include 'https://foo.bar/inbox' expect(subject.inboxes).to_not include 'https://foo.bar/inbox'
@ -114,7 +114,7 @@ describe StatusReachFinder do
context 'with follower' do context 'with follower' do
before do before do
Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted) Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted)
bob.follow!(alice) bob.follow!(alice)
end end
@ -124,9 +124,21 @@ describe StatusReachFinder do
end end
end end
context 'with non-follower' do context 'with follower but not local-distributable' do
before do before do
Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted) Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, delivery_local: false)
bob.follow!(alice)
end
it 'send status' do
expect(subject.inboxes).to include 'https://foo.bar/inbox'
expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox'
end
end
context 'with non-follower and non-relay' do
before do
Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted)
end end
it 'send status' do it 'send status' do
@ -137,7 +149,7 @@ describe StatusReachFinder do
context 'with pending' do context 'with pending' do
before do before do
Fabricate(:friend_domain, domain: 'foo.bar', active_state: :pending) Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :pending)
bob.follow!(alice) bob.follow!(alice)
end end
@ -147,9 +159,21 @@ describe StatusReachFinder do
end end
end end
context 'with unidirection from them' do
before do
Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :idle, passive_state: :accepted)
bob.follow!(alice)
end
it 'send status' do
expect(subject.inboxes).to_not include 'https://foo.bar/inbox'
expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox'
end
end
context 'when unavailable' do context 'when unavailable' do
before do before do
Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted, available: false) Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, available: false)
bob.follow!(alice) bob.follow!(alice)
end end
@ -161,7 +185,18 @@ describe StatusReachFinder do
context 'when distributable' do context 'when distributable' do
before do before do
Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted, passive_state: :accepted, pseudo_relay: true) Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true)
end
it 'send status' do
expect(subject.inboxes).to_not include 'https://foo.bar/inbox'
expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox'
end
end
context 'when distributable and following' do
before do
Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true)
bob.follow!(alice) bob.follow!(alice)
end end
@ -171,9 +206,9 @@ describe StatusReachFinder do
end end
end end
context 'when distributable and not following' do context 'when distributable reverse' do
before do before do
Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true) Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, pseudo_relay: true)
end end
it 'send status' do it 'send status' do
@ -181,10 +216,35 @@ describe StatusReachFinder do
expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox'
end end
end end
context 'when distributable but not local distributable' do
before do
Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true, delivery_local: false)
end
it 'send status' do
expect(subject.inboxes).to include 'https://foo.bar/inbox'
expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox'
end
end
context 'when distributable and following but not local distributable' do
before do
Fabricate(:friend_domain, domain: 'foo.bar', passive_state: :accepted, pseudo_relay: true, delivery_local: false)
bob.follow!(alice)
end
it 'send status' do
expect(subject.inboxes).to include 'https://foo.bar/inbox'
expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox'
end
end
end end
context 'when it contains distributable friend server' do context 'when it contains distributable friend server' do
before { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', available: true, active_state: :accepted, passive_state: :accepted, pseudo_relay: true) } before do
Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true)
end
it 'includes the inbox of the mentioned account' do it 'includes the inbox of the mentioned account' do
expect(subject.inboxes).to_not include 'https://foo.bar/inbox' expect(subject.inboxes).to_not include 'https://foo.bar/inbox'
@ -369,9 +429,11 @@ describe StatusReachFinder do
Fabricate(:friend_domain, domain: 'def.com', inbox_url: 'https://def.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true) Fabricate(:friend_domain, domain: 'def.com', inbox_url: 'https://def.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true)
Fabricate(:friend_domain, domain: 'ghi.com', inbox_url: 'https://ghi.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: false) Fabricate(:friend_domain, domain: 'ghi.com', inbox_url: 'https://ghi.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: false)
Fabricate(:friend_domain, domain: 'jkl.com', inbox_url: 'https://jkl.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: false, available: true) Fabricate(:friend_domain, domain: 'jkl.com', inbox_url: 'https://jkl.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: false, available: true)
Fabricate(:friend_domain, domain: 'mno.com', inbox_url: 'https://mno.com/inbox', active_state: :accepted, passive_state: :pending, pseudo_relay: true, available: true) Fabricate(:friend_domain, domain: 'mno.com', inbox_url: 'https://mno.com/inbox', active_state: :accepted, passive_state: :idle, pseudo_relay: true, available: true)
Fabricate(:friend_domain, domain: 'pqr.com', inbox_url: 'https://pqr.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true) Fabricate(:friend_domain, domain: 'pqr.com', inbox_url: 'https://pqr.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true)
Fabricate(:unavailable_domain, domain: 'pqr.com') Fabricate(:unavailable_domain, domain: 'pqr.com')
Fabricate(:friend_domain, domain: 'stu.com', inbox_url: 'https://stu.com/inbox', active_state: :idle, passive_state: :accepted, pseudo_relay: true, available: true)
Fabricate(:friend_domain, domain: 'vwx.com', inbox_url: 'https://vwx.com/inbox', active_state: :idle, passive_state: :accepted, pseudo_relay: true, available: true, delivery_local: false)
end end
it 'returns friend servers' do it 'returns friend servers' do
@ -387,8 +449,13 @@ describe StatusReachFinder do
expect(subject).to_not include 'https://jkl.com/inbox' expect(subject).to_not include 'https://jkl.com/inbox'
end end
it 'not contains no-mutual friends' do it 'contains no-mutual friends' do
expect(subject).to_not include 'https://mno.com/inbox' expect(subject).to include 'https://mno.com/inbox'
expect(subject).to include 'https://stu.com/inbox'
end
it 'not contains un local distable' do
expect(subject).to_not include 'https://vwx.com/inbox'
end end
it 'not contains unavailable domain friends' do it 'not contains unavailable domain friends' do

View file

@ -11,9 +11,11 @@ describe FriendDomain do
describe '#follow!' do describe '#follow!' do
it 'call inbox' do it 'call inbox' do
friend.update(active_state: :accepted, passive_state: :accepted)
friend.follow! friend.follow!
expect(friend.active_follow_activity_id).to_not be_nil expect(friend.active_follow_activity_id).to_not be_nil
expect(friend.i_am_pending?).to be true expect(friend.i_am_pending?).to be true
expect(friend.they_are_idle?).to be true
expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
id: friend.active_follow_activity_id, id: friend.active_follow_activity_id,
type: 'Follow', type: 'Follow',
@ -25,10 +27,11 @@ describe FriendDomain do
describe '#unfollow!' do describe '#unfollow!' do
it 'call inbox' do it 'call inbox' do
friend.update(active_follow_activity_id: 'ohagi') friend.update(active_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :accepted)
friend.unfollow! friend.unfollow!
expect(friend.active_follow_activity_id).to be_nil expect(friend.active_follow_activity_id).to be_nil
expect(friend.i_am_idle?).to be true expect(friend.i_am_idle?).to be true
expect(friend.they_are_idle?).to be true
expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
type: 'Undo', type: 'Undo',
object: { object: {
@ -43,9 +46,10 @@ describe FriendDomain do
describe '#accept!' do describe '#accept!' do
it 'call inbox' do it 'call inbox' do
friend.update(passive_follow_activity_id: 'ohagi', passive_state: :pending) friend.update(passive_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :pending)
friend.accept! friend.accept!
expect(friend.they_are_accepted?).to be true expect(friend.they_are_accepted?).to be true
expect(friend.i_am_idle?).to be true
expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
id: 'ohagi#accepts/friends', id: 'ohagi#accepts/friends',
type: 'Accept', type: 'Accept',
@ -57,9 +61,10 @@ describe FriendDomain do
describe '#reject!' do describe '#reject!' do
it 'call inbox' do it 'call inbox' do
friend.update(passive_follow_activity_id: 'ohagi', passive_state: :pending) friend.update(passive_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :pending)
friend.reject! friend.reject!
expect(friend.they_are_rejected?).to be true expect(friend.they_are_rejected?).to be true
expect(friend.i_am_idle?).to be true
expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
id: 'ohagi#rejects/friends', id: 'ohagi#rejects/friends',
type: 'Reject', type: 'Reject',

View file

@ -54,6 +54,14 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
end end
context 'when limited' do context 'when limited' do
let(:searchable_by) { 'kmyblue:Limited' }
it 'searchability is limited' do
expect(subject.searchability).to eq 'limited'
end
end
context 'when limited old spec' do
let(:searchable_by) { 'as:Limited' } let(:searchable_by) { 'as:Limited' }
it 'searchability is limited' do it 'searchability is limited' do

View file

@ -295,6 +295,18 @@ RSpec.describe DeliveryAntennaService, type: :service do
end end
end end
context 'when public_unlisted searchability' do
let(:searchability) { :public_unlisted }
it 'detecting antenna' do
expect(antenna_feed_of(antenna)).to include status.id
end
it 'not detecting antenna' do
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
context 'when private searchability' do context 'when private searchability' do
let(:searchability) { :private } let(:searchability) { :private }
@ -317,6 +329,15 @@ RSpec.describe DeliveryAntennaService, type: :service do
end end
end end
context 'when public_unlisted searchability' do
let(:searchability) { :public_unlisted }
it 'detecting antenna' do
expect(antenna_feed_of(antenna)).to include status.id
expect(antenna_feed_of(empty_antenna)).to include status.id
end
end
context 'when private searchability' do context 'when private searchability' do
let(:searchability) { :private } let(:searchability) { :private }

View file

@ -36,7 +36,7 @@ RSpec.describe ReportService, type: :service do
expect(report.uri).to_not be_nil expect(report.uri).to_not be_nil
end end
context 'when reporting a reply' do context 'when reporting a reply on a different remote server' do
let(:remote_thread_account) { Fabricate(:account, domain: 'foo.com', protocol: :activitypub, inbox_url: 'http://foo.com/inbox') } let(:remote_thread_account) { Fabricate(:account, domain: 'foo.com', protocol: :activitypub, inbox_url: 'http://foo.com/inbox') }
let(:reported_status) { Fabricate(:status, account: remote_account, thread: Fabricate(:status, account: remote_thread_account)) } let(:reported_status) { Fabricate(:status, account: remote_account, thread: Fabricate(:status, account: remote_thread_account)) }
@ -67,6 +67,25 @@ RSpec.describe ReportService, type: :service do
end end
end end
end end
context 'when reporting a reply on the same remote server as the person being replied-to' do
let(:remote_thread_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
let(:reported_status) { Fabricate(:status, account: remote_account, thread: Fabricate(:status, account: remote_thread_account)) }
context 'when forward_to_domains includes both the replied-to domain and the origin domain' do
it 'sends ActivityPub payload only once' do
subject.call(source_account, remote_account, status_ids: [reported_status.id], forward: forward, forward_to_domains: [remote_account.domain])
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
end
end
context 'when forward_to_domains does not include the replied-to domain' do
it 'sends ActivityPub payload only once' do
subject.call(source_account, remote_account, status_ids: [reported_status.id], forward: forward)
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
end
end
end
end end
context 'when forward is false' do context 'when forward is false' do