Merge commit '8a0261c51caf76b6d12e3801da471759c31c9608' into glitch-soc/merge-upstream

This commit is contained in:
Claire
2026-03-04 12:40:16 +01:00
28 changed files with 278 additions and 65 deletions

View File

@@ -294,7 +294,7 @@ GEM
activesupport (>= 5.1) activesupport (>= 5.1)
haml (>= 4.0.6) haml (>= 4.0.6)
railties (>= 5.1) railties (>= 5.1)
haml_lint (0.71.0) haml_lint (0.72.0)
haml (>= 5.0) haml (>= 5.0)
parallel (~> 1.10) parallel (~> 1.10)
rainbow rainbow
@@ -628,7 +628,7 @@ GEM
psych (5.3.1) psych (5.3.1)
date date
stringio stringio
public_suffix (7.0.2) public_suffix (7.0.5)
puma (7.2.0) puma (7.2.0)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.5.2) pundit (2.5.2)
@@ -892,7 +892,7 @@ GEM
unf (~> 0.1.0) unf (~> 0.1.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
tzinfo-data (1.2025.3) tzinfo-data (1.2026.1)
tzinfo (>= 1.0.0) tzinfo (>= 1.0.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext

View File

@@ -5,6 +5,7 @@
.filterSelectButton { .filterSelectButton {
appearance: none; appearance: none;
border: none; border: none;
color: inherit;
background: none; background: none;
padding: 8px 0; padding: 8px 0;
font-size: 15px; font-size: 15px;

View File

@@ -105,8 +105,8 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({
); );
}, [collection, dispatch]); }, [collection, dispatch]);
const location = useLocation<{ newCollection?: boolean }>(); const location = useLocation<{ newCollection?: boolean } | undefined>();
const wasJustCreated = location.state.newCollection; const wasJustCreated = location.state?.newCollection;
useEffect(() => { useEffect(() => {
if (wasJustCreated) { if (wasJustCreated) {
handleShare(); handleShare();

View File

@@ -40,8 +40,8 @@ export const CollectionShareModal: React.FC<{
}> = ({ collection, onClose }) => { }> = ({ collection, onClose }) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const location = useLocation<{ newCollection?: boolean }>(); const location = useLocation<{ newCollection?: boolean } | undefined>();
const isNew = !!location.state.newCollection; const isNew = !!location.state?.newCollection;
const isOwnCollection = collection.account_id === me; const isOwnCollection = collection.account_id === me;
const collectionLink = `${window.location.origin}/collections/${collection.id}`; const collectionLink = `${window.location.origin}/collections/${collection.id}`;

View File

@@ -536,7 +536,7 @@
"empty_column.follow_requests": "Du har endnu ingen følgeanmodninger. Når du modtager én, vil den dukke op her.", "empty_column.follow_requests": "Du har endnu ingen følgeanmodninger. Når du modtager én, vil den dukke op her.",
"empty_column.followed_tags": "Ingen hashtags følges endnu. Når det sker, vil de fremgå her.", "empty_column.followed_tags": "Ingen hashtags følges endnu. Når det sker, vil de fremgå her.",
"empty_column.hashtag": "Der er intet med dette hashtag endnu.", "empty_column.hashtag": "Der er intet med dette hashtag endnu.",
"empty_column.home": "Din hjem-tidslinje er tom! Følg nogle personer, for at fylde den op.", "empty_column.home": "Din hjem-tidslinje er tom! Følg flere personer, for at fylde den op.",
"empty_column.list": "Der er ikke noget på denne liste endnu. Når medlemmer af denne liste udgiver nye indlæg, vil de blive vist her.", "empty_column.list": "Der er ikke noget på denne liste endnu. Når medlemmer af denne liste udgiver nye indlæg, vil de blive vist her.",
"empty_column.mutes": "Du har endnu ikke skjult nogle brugere.", "empty_column.mutes": "Du har endnu ikke skjult nogle brugere.",
"empty_column.notification_requests": "Alt er klar! Der er intet her. Når der modtages nye notifikationer, fremgår de her jævnfør dine indstillinger.", "empty_column.notification_requests": "Alt er klar! Der er intet her. Når der modtages nye notifikationer, fremgår de her jævnfør dine indstillinger.",
@@ -683,7 +683,7 @@
"keyboard_shortcuts.direct": "Åbn kolonne med private omtaler", "keyboard_shortcuts.direct": "Åbn kolonne med private omtaler",
"keyboard_shortcuts.down": "Flyt nedad på listen", "keyboard_shortcuts.down": "Flyt nedad på listen",
"keyboard_shortcuts.enter": "Åbn indlæg", "keyboard_shortcuts.enter": "Åbn indlæg",
"keyboard_shortcuts.explore": "Åbn Trender-tidslinjen", "keyboard_shortcuts.explore": "Åbn trender-tidslinjen",
"keyboard_shortcuts.favourite": "Føj indlæg til favoritter", "keyboard_shortcuts.favourite": "Føj indlæg til favoritter",
"keyboard_shortcuts.favourites": "Åbn favoritlisten", "keyboard_shortcuts.favourites": "Åbn favoritlisten",
"keyboard_shortcuts.federated": "Åbn fødereret tidslinje", "keyboard_shortcuts.federated": "Åbn fødereret tidslinje",

View File

@@ -1074,7 +1074,7 @@
"sign_in_banner.sign_in": "Kirjaudu", "sign_in_banner.sign_in": "Kirjaudu",
"sign_in_banner.sso_redirect": "Kirjaudu tai rekisteröidy", "sign_in_banner.sso_redirect": "Kirjaudu tai rekisteröidy",
"skip_links.hotkey": "<span>Pikanäppäin</span> {hotkey}", "skip_links.hotkey": "<span>Pikanäppäin</span> {hotkey}",
"skip_links.skip_to_content": "Siitty pääsisältöön", "skip_links.skip_to_content": "Siirry pääsisältöön",
"skip_links.skip_to_navigation": "Siirry päänavigaatioon", "skip_links.skip_to_navigation": "Siirry päänavigaatioon",
"status.admin_account": "Avaa tilin @{name} moderointinäkymä", "status.admin_account": "Avaa tilin @{name} moderointinäkymä",
"status.admin_domain": "Avaa palvelimen {domain} moderointinäkymä", "status.admin_domain": "Avaa palvelimen {domain} moderointinäkymä",

View File

@@ -173,6 +173,7 @@
"account_edit.profile_tab.subtitle": "Tillaga spjøldrini á vanganum hjá tær og tað, tey vísa.", "account_edit.profile_tab.subtitle": "Tillaga spjøldrini á vanganum hjá tær og tað, tey vísa.",
"account_edit.profile_tab.title": "Stillingar fyri spjøldur á vanga", "account_edit.profile_tab.title": "Stillingar fyri spjøldur á vanga",
"account_edit.save": "Goym", "account_edit.save": "Goym",
"account_edit_tags.add_tag": "Legg #{tagName} afturat",
"account_edit_tags.column_title": "Rætta sermerkt frámerki", "account_edit_tags.column_title": "Rætta sermerkt frámerki",
"account_edit_tags.help_text": "Sermerkt frámerki hjálpa brúkarum at varnast og virka saman við vanga tínum. Tey síggjast sum filtur á virksemisvísingini av vanga tínum.", "account_edit_tags.help_text": "Sermerkt frámerki hjálpa brúkarum at varnast og virka saman við vanga tínum. Tey síggjast sum filtur á virksemisvísingini av vanga tínum.",
"account_edit_tags.search_placeholder": "Áset eitt frámerki…", "account_edit_tags.search_placeholder": "Áset eitt frámerki…",
@@ -682,6 +683,7 @@
"keyboard_shortcuts.direct": "Lat teigin við privatum umrøðum upp", "keyboard_shortcuts.direct": "Lat teigin við privatum umrøðum upp",
"keyboard_shortcuts.down": "Flyt niðureftir listanum", "keyboard_shortcuts.down": "Flyt niðureftir listanum",
"keyboard_shortcuts.enter": "Opna uppslag", "keyboard_shortcuts.enter": "Opna uppslag",
"keyboard_shortcuts.explore": "Lat rás við vælumtóktum postum upp",
"keyboard_shortcuts.favourite": "Dáma post", "keyboard_shortcuts.favourite": "Dáma post",
"keyboard_shortcuts.favourites": "Lat listan av dámdum postum upp", "keyboard_shortcuts.favourites": "Lat listan av dámdum postum upp",
"keyboard_shortcuts.federated": "Lat felags tíðslinju upp", "keyboard_shortcuts.federated": "Lat felags tíðslinju upp",
@@ -1071,6 +1073,9 @@
"sign_in_banner.mastodon_is": "Mastodon er best mátin at fylgja við í tí, sum hendir.", "sign_in_banner.mastodon_is": "Mastodon er best mátin at fylgja við í tí, sum hendir.",
"sign_in_banner.sign_in": "Rita inn", "sign_in_banner.sign_in": "Rita inn",
"sign_in_banner.sso_redirect": "Rita inn ella Skráset teg", "sign_in_banner.sso_redirect": "Rita inn ella Skráset teg",
"skip_links.hotkey": "<span>Snarknappur</span> {hotkey}",
"skip_links.skip_to_content": "Far til høvuðsinnihald",
"skip_links.skip_to_navigation": "Far til høvuðs-navigatión",
"status.admin_account": "Lat kjakleiðaramarkamót upp fyri @{name}", "status.admin_account": "Lat kjakleiðaramarkamót upp fyri @{name}",
"status.admin_domain": "Lat umsjónarmarkamót upp fyri {domain}", "status.admin_domain": "Lat umsjónarmarkamót upp fyri {domain}",
"status.admin_status": "Lat hendan postin upp í kjakleiðaramarkamótinum", "status.admin_status": "Lat hendan postin upp í kjakleiðaramarkamótinum",

View File

@@ -173,6 +173,7 @@
"account_edit.profile_tab.subtitle": "Personnaliser les onglets de votre profil et leur contenu.", "account_edit.profile_tab.subtitle": "Personnaliser les onglets de votre profil et leur contenu.",
"account_edit.profile_tab.title": "Paramètres de l'onglet du profil", "account_edit.profile_tab.title": "Paramètres de l'onglet du profil",
"account_edit.save": "Enregistrer", "account_edit.save": "Enregistrer",
"account_edit_tags.add_tag": "Ajouter #{tagName}",
"account_edit_tags.column_title": "Modifier les hashtags mis en avant", "account_edit_tags.column_title": "Modifier les hashtags mis en avant",
"account_edit_tags.help_text": "Les hashtags mis en avant aident les personnes à découvrir et interagir avec votre profil. Ils apparaissent comme des filtres dans la vue « Activité » de votre profil.", "account_edit_tags.help_text": "Les hashtags mis en avant aident les personnes à découvrir et interagir avec votre profil. Ils apparaissent comme des filtres dans la vue « Activité » de votre profil.",
"account_edit_tags.search_placeholder": "Saisir un hashtag…", "account_edit_tags.search_placeholder": "Saisir un hashtag…",
@@ -1073,6 +1074,8 @@
"sign_in_banner.sign_in": "Se connecter", "sign_in_banner.sign_in": "Se connecter",
"sign_in_banner.sso_redirect": "Se connecter ou sinscrire", "sign_in_banner.sso_redirect": "Se connecter ou sinscrire",
"skip_links.hotkey": "<span>Raccourci</span> {hotkey}", "skip_links.hotkey": "<span>Raccourci</span> {hotkey}",
"skip_links.skip_to_content": "Accéder au contenu principal",
"skip_links.skip_to_navigation": "Accéder à la navigation principale",
"status.admin_account": "Ouvrir linterface de modération pour @{name}", "status.admin_account": "Ouvrir linterface de modération pour @{name}",
"status.admin_domain": "Ouvrir linterface de modération pour {domain}", "status.admin_domain": "Ouvrir linterface de modération pour {domain}",
"status.admin_status": "Ouvrir ce message dans linterface de modération", "status.admin_status": "Ouvrir ce message dans linterface de modération",

View File

@@ -173,6 +173,7 @@
"account_edit.profile_tab.subtitle": "Personnaliser les onglets de votre profil et leur contenu.", "account_edit.profile_tab.subtitle": "Personnaliser les onglets de votre profil et leur contenu.",
"account_edit.profile_tab.title": "Paramètres de l'onglet du profil", "account_edit.profile_tab.title": "Paramètres de l'onglet du profil",
"account_edit.save": "Enregistrer", "account_edit.save": "Enregistrer",
"account_edit_tags.add_tag": "Ajouter #{tagName}",
"account_edit_tags.column_title": "Modifier les hashtags mis en avant", "account_edit_tags.column_title": "Modifier les hashtags mis en avant",
"account_edit_tags.help_text": "Les hashtags mis en avant aident les personnes à découvrir et interagir avec votre profil. Ils apparaissent comme des filtres dans la vue « Activité » de votre profil.", "account_edit_tags.help_text": "Les hashtags mis en avant aident les personnes à découvrir et interagir avec votre profil. Ils apparaissent comme des filtres dans la vue « Activité » de votre profil.",
"account_edit_tags.search_placeholder": "Saisir un hashtag…", "account_edit_tags.search_placeholder": "Saisir un hashtag…",
@@ -1073,6 +1074,8 @@
"sign_in_banner.sign_in": "Se connecter", "sign_in_banner.sign_in": "Se connecter",
"sign_in_banner.sso_redirect": "Se connecter ou sinscrire", "sign_in_banner.sso_redirect": "Se connecter ou sinscrire",
"skip_links.hotkey": "<span>Raccourci</span> {hotkey}", "skip_links.hotkey": "<span>Raccourci</span> {hotkey}",
"skip_links.skip_to_content": "Accéder au contenu principal",
"skip_links.skip_to_navigation": "Accéder à la navigation principale",
"status.admin_account": "Ouvrir linterface de modération pour @{name}", "status.admin_account": "Ouvrir linterface de modération pour @{name}",
"status.admin_domain": "Ouvrir linterface de modération pour {domain}", "status.admin_domain": "Ouvrir linterface de modération pour {domain}",
"status.admin_status": "Ouvrir ce message dans linterface de modération", "status.admin_status": "Ouvrir ce message dans linterface de modération",

View File

@@ -44,9 +44,11 @@
"account.familiar_followers_two": "{name1} és {name2} követi", "account.familiar_followers_two": "{name1} és {name2} követi",
"account.featured": "Kiemelt", "account.featured": "Kiemelt",
"account.featured.accounts": "Profilok", "account.featured.accounts": "Profilok",
"account.featured.collections": "Gyűjtemények",
"account.featured.hashtags": "Hashtagek", "account.featured.hashtags": "Hashtagek",
"account.featured_tags.last_status_at": "Legutolsó bejegyzés ideje: {date}", "account.featured_tags.last_status_at": "Legutolsó bejegyzés ideje: {date}",
"account.featured_tags.last_status_never": "Nincs bejegyzés", "account.featured_tags.last_status_never": "Nincs bejegyzés",
"account.field_overflow": "Teljes tartalom megjelenítése",
"account.filters.all": "Összes tevékenység", "account.filters.all": "Összes tevékenység",
"account.filters.boosts_toggle": "Megtolások megjelenítése", "account.filters.boosts_toggle": "Megtolások megjelenítése",
"account.filters.posts_boosts": "Bejegyzések és megtolások", "account.filters.posts_boosts": "Bejegyzések és megtolások",
@@ -144,17 +146,30 @@
"account_edit.bio.title": "Bemutatkozás", "account_edit.bio.title": "Bemutatkozás",
"account_edit.bio_modal.add_title": "Bemutatkozás hozzáadása", "account_edit.bio_modal.add_title": "Bemutatkozás hozzáadása",
"account_edit.bio_modal.edit_title": "Bemutatkozás szerkesztése", "account_edit.bio_modal.edit_title": "Bemutatkozás szerkesztése",
"account_edit.button.add": "{item} hozzáadása",
"account_edit.button.delete": "{item} törlése",
"account_edit.button.edit": "{item} szerkesztése",
"account_edit.char_counter": "{currentLength}/{maxLength} karakter", "account_edit.char_counter": "{currentLength}/{maxLength} karakter",
"account_edit.column_button": "Kész", "account_edit.column_button": "Kész",
"account_edit.column_title": "Profil szerkesztése", "account_edit.column_title": "Profil szerkesztése",
"account_edit.custom_fields.title": "Egyéni mezők", "account_edit.custom_fields.title": "Egyéni mezők",
"account_edit.display_name.placeholder": "A megjelenítendő név az, ahogy a neved megjelenik a profilodon és az idővonalakon.", "account_edit.display_name.placeholder": "A megjelenítendő név az, ahogy a neved megjelenik a profilodon és az idővonalakon.",
"account_edit.display_name.title": "Megjelenítendő név", "account_edit.display_name.title": "Megjelenítendő név",
"account_edit.featured_hashtags.item": "hashtagek",
"account_edit.featured_hashtags.placeholder": "Segíts másoknak, hogy azonosíthassák a kedvenc témáid, és gyorsan elérjék azokat.",
"account_edit.featured_hashtags.title": "Kiemelt hashtagek", "account_edit.featured_hashtags.title": "Kiemelt hashtagek",
"account_edit.name_modal.add_title": "Megjelenítendő név hozzáadása", "account_edit.name_modal.add_title": "Megjelenítendő név hozzáadása",
"account_edit.name_modal.edit_title": "Megjelenítendő név szerkesztése", "account_edit.name_modal.edit_title": "Megjelenítendő név szerkesztése",
"account_edit.profile_tab.button_label": "Testreszabás",
"account_edit.profile_tab.hint.description": "Ezek a beállítások szabják testre, hogy a felhasználók mit látnak a(z) {server} kiszolgálón a hivatalos alkalmazásokban, de nem biztos, hogy a külső kiszolgálókon vagy alkalmazásokban is érvényesek lesznek.",
"account_edit.profile_tab.hint.title": "A megjelenítés eltérő lehet",
"account_edit.profile_tab.show_featured.description": "A „Kiemelt” egy nem kötelező lap, ahol más fiókokat mutathatsz be.",
"account_edit.profile_tab.show_featured.title": "„Kiemelt” lap megjelenítése",
"account_edit.profile_tab.show_media.description": "A „Média” egy nem kötelező lap, amely a képeket vagy videókat tartalmazó bejegyzéseidet jeleníti meg.",
"account_edit.profile_tab.show_media.title": "„Média” lap megjelenítése",
"account_edit.profile_tab.title": "Profil lap beállításai", "account_edit.profile_tab.title": "Profil lap beállításai",
"account_edit.save": "Mentés", "account_edit.save": "Mentés",
"account_edit_tags.add_tag": "#{tagName} hozzáadása",
"account_note.placeholder": "Kattintás jegyzet hozzáadásához", "account_note.placeholder": "Kattintás jegyzet hozzáadásához",
"admin.dashboard.daily_retention": "Napi regisztráció utáni felhasználómegtartási arány", "admin.dashboard.daily_retention": "Napi regisztráció utáni felhasználómegtartási arány",
"admin.dashboard.monthly_retention": "Havi regisztráció utáni felhasználómegtartási arány", "admin.dashboard.monthly_retention": "Havi regisztráció utáni felhasználómegtartási arány",

View File

@@ -47,8 +47,12 @@
"account.featured.hashtags": "해시태그", "account.featured.hashtags": "해시태그",
"account.featured_tags.last_status_at": "{date}에 마지막으로 게시", "account.featured_tags.last_status_at": "{date}에 마지막으로 게시",
"account.featured_tags.last_status_never": "게시물 없음", "account.featured_tags.last_status_never": "게시물 없음",
"account.field_overflow": "내용 전체 보기",
"account.filters.all": "모든 활동", "account.filters.all": "모든 활동",
"account.filters.boosts_toggle": "부스트 보기", "account.filters.boosts_toggle": "부스트 보기",
"account.filters.posts_boosts": "게시물과 부스트",
"account.filters.posts_only": "게시물",
"account.filters.posts_replies": "게시물과 답장",
"account.filters.replies_toggle": "답글 보기", "account.filters.replies_toggle": "답글 보기",
"account.follow": "팔로우", "account.follow": "팔로우",
"account.follow_back": "맞팔로우", "account.follow_back": "맞팔로우",
@@ -68,14 +72,23 @@
"account.go_to_profile": "프로필로 이동", "account.go_to_profile": "프로필로 이동",
"account.hide_reblogs": "@{name}의 부스트를 숨기기", "account.hide_reblogs": "@{name}의 부스트를 숨기기",
"account.in_memoriam": "고인의 계정입니다.", "account.in_memoriam": "고인의 계정입니다.",
"account.joined_long": "{date}에 가입함",
"account.joined_short": "가입", "account.joined_short": "가입",
"account.languages": "구독한 언어 변경", "account.languages": "구독한 언어 변경",
"account.link_verified_on": "{date}에 이 링크의 소유권이 확인 됨", "account.link_verified_on": "{date}에 이 링크의 소유권이 확인 됨",
"account.locked_info": "이 계정의 프라이버시 설정은 잠금으로 설정되어 있습니다. 계정 소유자가 수동으로 팔로워를 승인합니다.", "account.locked_info": "이 계정의 프라이버시 설정은 잠금으로 설정되어 있습니다. 계정 소유자가 수동으로 팔로워를 승인합니다.",
"account.media": "미디어", "account.media": "미디어",
"account.mention": "@{name} 님에게 멘션", "account.mention": "@{name} 님에게 멘션",
"account.menu.add_to_list": "리스트에 추가…",
"account.menu.block": "계정 차단",
"account.menu.block_domain": "{domain} 차단",
"account.menu.copied": "계정 링크를 복사했습니다",
"account.menu.copy": "링크 복사하기", "account.menu.copy": "링크 복사하기",
"account.menu.mention": "멘션", "account.menu.mention": "멘션",
"account.menu.note.description": "나에게만 보입니다",
"account.menu.open_original_page": "{domain}에서 보기",
"account.menu.remove_follower": "팔로워 제거",
"account.menu.report": "계정 신고",
"account.menu.share": "공유하기…", "account.menu.share": "공유하기…",
"account.moved_to": "{name} 님은 자신의 새 계정이 다음과 같다고 표시했습니다:", "account.moved_to": "{name} 님은 자신의 새 계정이 다음과 같다고 표시했습니다:",
"account.mute": "@{name} 뮤트", "account.mute": "@{name} 뮤트",
@@ -85,6 +98,9 @@
"account.muting": "뮤트함", "account.muting": "뮤트함",
"account.mutual": "서로 팔로우", "account.mutual": "서로 팔로우",
"account.no_bio": "제공된 설명이 없습니다.", "account.no_bio": "제공된 설명이 없습니다.",
"account.node_modal.save": "저장",
"account.node_modal.title": "개인 메모 추가",
"account.note.edit_button": "편집",
"account.open_original_page": "원본 페이지 열기", "account.open_original_page": "원본 페이지 열기",
"account.posts": "게시물", "account.posts": "게시물",
"account.posts_with_replies": "게시물과 답장", "account.posts_with_replies": "게시물과 답장",
@@ -104,6 +120,15 @@
"account.unmute": "@{name} 뮤트 해제", "account.unmute": "@{name} 뮤트 해제",
"account.unmute_notifications_short": "알림 뮤트 해제", "account.unmute_notifications_short": "알림 뮤트 해제",
"account.unmute_short": "뮤트 해제", "account.unmute_short": "뮤트 해제",
"account_edit.bio.title": "자기소개",
"account_edit.bio_modal.add_title": "자기소개 추가",
"account_edit.bio_modal.edit_title": "자기소개 편집",
"account_edit.button.add": "{item} 추가",
"account_edit.button.delete": "{item} 제거",
"account_edit.button.edit": "{item} 편집",
"account_edit.char_counter": "{currentLength}/{maxLength} 글자",
"account_edit.column_button": "완료",
"account_edit.column_title": "프로필 편집",
"account_note.placeholder": "클릭하여 노트 추가", "account_note.placeholder": "클릭하여 노트 추가",
"admin.dashboard.daily_retention": "가입 후 일별 사용자 유지율", "admin.dashboard.daily_retention": "가입 후 일별 사용자 유지율",
"admin.dashboard.monthly_retention": "가입 후 월별 사용자 유지율", "admin.dashboard.monthly_retention": "가입 후 월별 사용자 유지율",

View File

@@ -161,7 +161,11 @@
"account_edit.featured_hashtags.title": "Etiquetas em destaque", "account_edit.featured_hashtags.title": "Etiquetas em destaque",
"account_edit.name_modal.add_title": "Adicionar nome a mostrar", "account_edit.name_modal.add_title": "Adicionar nome a mostrar",
"account_edit.name_modal.edit_title": "Editar o nome a mostrar", "account_edit.name_modal.edit_title": "Editar o nome a mostrar",
"account_edit.profile_tab.button_label": "Personalizar",
"account_edit.profile_tab.hint.description": "Estas configurações personalizam o que os utilizadores veem no {server} nas aplicações oficiais, mas podem não se aplicar aos utilizadores de outros servidores nem aplicações de terceiros.",
"account_edit.profile_tab.hint.title": "A apresentação ainda pode variar",
"account_edit.save": "Guardar", "account_edit.save": "Guardar",
"account_edit_tags.add_tag": "Adicionar #{tagName}",
"account_edit_tags.column_title": "Editar etiquetas em destaque", "account_edit_tags.column_title": "Editar etiquetas em destaque",
"account_edit_tags.help_text": "As etiquetas destacadas ajudam os utilizadores a descobrir e interagir com o seu perfil. Aparecem como filtros na vista de atividade da sua página de perfil.", "account_edit_tags.help_text": "As etiquetas destacadas ajudam os utilizadores a descobrir e interagir com o seu perfil. Aparecem como filtros na vista de atividade da sua página de perfil.",
"account_edit_tags.search_placeholder": "Insira uma etiqueta…", "account_edit_tags.search_placeholder": "Insira uma etiqueta…",
@@ -269,6 +273,12 @@
"closed_registrations_modal.find_another_server": "Procurar outro servidor", "closed_registrations_modal.find_another_server": "Procurar outro servidor",
"closed_registrations_modal.preamble": "O Mastodon é descentralizado, por isso não importa onde a tua conta é criada, pois continuarás a poder acompanhar e interagir com qualquer um neste servidor. Podes até alojar o teu próprio servidor!", "closed_registrations_modal.preamble": "O Mastodon é descentralizado, por isso não importa onde a tua conta é criada, pois continuarás a poder acompanhar e interagir com qualquer um neste servidor. Podes até alojar o teu próprio servidor!",
"closed_registrations_modal.title": "Criar uma conta no Mastodon", "closed_registrations_modal.title": "Criar uma conta no Mastodon",
"collection.share_modal.share_via_post": "Publicar no Mastodon",
"collection.share_modal.share_via_system": "Compartilhar com…",
"collection.share_modal.title": "Partilhar coleção",
"collection.share_modal.title_new": "Partilhe a sua nova coleção!",
"collection.share_template_other": "Veja esta coleção interessante: {link}",
"collection.share_template_own": "Veja a minha nova coleção: {link}",
"collections.account_count": "{count, plural, one {# conta} other {# contas}}", "collections.account_count": "{count, plural, one {# conta} other {# contas}}",
"collections.accounts.empty_description": "Adicione até {count} contas que segue", "collections.accounts.empty_description": "Adicione até {count} contas que segue",
"collections.accounts.empty_title": "Esta coleção está vazia", "collections.accounts.empty_title": "Esta coleção está vazia",
@@ -287,6 +297,8 @@
"collections.delete_collection": "Eliminar coleção", "collections.delete_collection": "Eliminar coleção",
"collections.description_length_hint": "Limite de 100 caracteres", "collections.description_length_hint": "Limite de 100 caracteres",
"collections.detail.accounts_heading": "Contas", "collections.detail.accounts_heading": "Contas",
"collections.detail.curated_by_author": "Curado por {author}",
"collections.detail.curated_by_you": "Curado por si",
"collections.detail.loading": "A carregar a coleção…", "collections.detail.loading": "A carregar a coleção…",
"collections.detail.share": "Partilhar esta coleção", "collections.detail.share": "Partilhar esta coleção",
"collections.edit_details": "Editar detalhes", "collections.edit_details": "Editar detalhes",

View File

@@ -173,6 +173,7 @@
"account_edit.profile_tab.subtitle": "Profilinizdeki sekmeleri ve bunların görüntülediği bilgileri özelleştirin.", "account_edit.profile_tab.subtitle": "Profilinizdeki sekmeleri ve bunların görüntülediği bilgileri özelleştirin.",
"account_edit.profile_tab.title": "Profil sekme ayarları", "account_edit.profile_tab.title": "Profil sekme ayarları",
"account_edit.save": "Kaydet", "account_edit.save": "Kaydet",
"account_edit_tags.add_tag": "#{tagName} ekle",
"account_edit_tags.column_title": "Öne çıkarılmış etiketleri düzenle", "account_edit_tags.column_title": "Öne çıkarılmış etiketleri düzenle",
"account_edit_tags.help_text": "Öne çıkan etiketler kullanıcıların profilinizi keşfetmesine ve etkileşim kurmasına yardımcı olur. Profil sayfanızın Etkinlik görünümünde filtreler olarak görünürler.", "account_edit_tags.help_text": "Öne çıkan etiketler kullanıcıların profilinizi keşfetmesine ve etkileşim kurmasına yardımcı olur. Profil sayfanızın Etkinlik görünümünde filtreler olarak görünürler.",
"account_edit_tags.search_placeholder": "Bir etiket girin…", "account_edit_tags.search_placeholder": "Bir etiket girin…",
@@ -682,6 +683,7 @@
"keyboard_shortcuts.direct": "Özel bahsetmeler sütununu aç", "keyboard_shortcuts.direct": "Özel bahsetmeler sütununu aç",
"keyboard_shortcuts.down": "Listede aşağıya inmek için", "keyboard_shortcuts.down": "Listede aşağıya inmek için",
"keyboard_shortcuts.enter": "Gönderiyi açınız", "keyboard_shortcuts.enter": "Gönderiyi açınız",
"keyboard_shortcuts.explore": "Öne çıkanlar zaman çizelgesini aç",
"keyboard_shortcuts.favourite": "Gönderiyi favorilerine ekle", "keyboard_shortcuts.favourite": "Gönderiyi favorilerine ekle",
"keyboard_shortcuts.favourites": "Gözde listeni aç", "keyboard_shortcuts.favourites": "Gözde listeni aç",
"keyboard_shortcuts.federated": "Federe akışı aç", "keyboard_shortcuts.federated": "Federe akışı aç",
@@ -1071,6 +1073,9 @@
"sign_in_banner.mastodon_is": "Neler olup bittiğini izlemenin en iyi aracı Mastodon'dur.", "sign_in_banner.mastodon_is": "Neler olup bittiğini izlemenin en iyi aracı Mastodon'dur.",
"sign_in_banner.sign_in": "Giriş yap", "sign_in_banner.sign_in": "Giriş yap",
"sign_in_banner.sso_redirect": "Giriş yap veya kaydol", "sign_in_banner.sso_redirect": "Giriş yap veya kaydol",
"skip_links.hotkey": "<span>Kısayol tuşu</span> {hotkey}",
"skip_links.skip_to_content": "Ana içeriğe git",
"skip_links.skip_to_navigation": "Ana gezinmeye git",
"status.admin_account": "@{name} için denetim arayüzünü açın", "status.admin_account": "@{name} için denetim arayüzünü açın",
"status.admin_domain": "{domain} için denetim arayüzünü açın", "status.admin_domain": "{domain} için denetim arayüzünü açın",
"status.admin_status": "Denetim arayüzünde bu gönderiyi açın", "status.admin_status": "Denetim arayüzünde bu gönderiyi açın",

View File

@@ -173,6 +173,7 @@
"account_edit.profile_tab.subtitle": "自定义你个人资料的标签页及其显示的内容。", "account_edit.profile_tab.subtitle": "自定义你个人资料的标签页及其显示的内容。",
"account_edit.profile_tab.title": "个人资料标签页设置", "account_edit.profile_tab.title": "个人资料标签页设置",
"account_edit.save": "保存", "account_edit.save": "保存",
"account_edit_tags.add_tag": "添加 #{tagName}",
"account_edit_tags.column_title": "编辑精选话题标签", "account_edit_tags.column_title": "编辑精选话题标签",
"account_edit_tags.help_text": "精选话题标签可以帮助他人发现并与你的个人资料互动。这些标签会作为过滤器条件出现在你个人资料页面的活动视图中。", "account_edit_tags.help_text": "精选话题标签可以帮助他人发现并与你的个人资料互动。这些标签会作为过滤器条件出现在你个人资料页面的活动视图中。",
"account_edit_tags.search_placeholder": "输入话题标签…", "account_edit_tags.search_placeholder": "输入话题标签…",
@@ -682,6 +683,7 @@
"keyboard_shortcuts.direct": "打开私下提及栏", "keyboard_shortcuts.direct": "打开私下提及栏",
"keyboard_shortcuts.down": "在列表中让光标下移", "keyboard_shortcuts.down": "在列表中让光标下移",
"keyboard_shortcuts.enter": "展开嘟文", "keyboard_shortcuts.enter": "展开嘟文",
"keyboard_shortcuts.explore": "打开当前热门时间线",
"keyboard_shortcuts.favourite": "喜欢嘟文", "keyboard_shortcuts.favourite": "喜欢嘟文",
"keyboard_shortcuts.favourites": "打开喜欢列表", "keyboard_shortcuts.favourites": "打开喜欢列表",
"keyboard_shortcuts.federated": "打开跨站时间线", "keyboard_shortcuts.federated": "打开跨站时间线",
@@ -1071,6 +1073,9 @@
"sign_in_banner.mastodon_is": "Mastodon 是了解最新动态的最佳途径。", "sign_in_banner.mastodon_is": "Mastodon 是了解最新动态的最佳途径。",
"sign_in_banner.sign_in": "登录", "sign_in_banner.sign_in": "登录",
"sign_in_banner.sso_redirect": "登录或注册", "sign_in_banner.sso_redirect": "登录或注册",
"skip_links.hotkey": "<span>快捷键</span> {hotkey}",
"skip_links.skip_to_content": "跳转到主内容",
"skip_links.skip_to_navigation": "跳转到主导航",
"status.admin_account": "打开 @{name} 的管理界面", "status.admin_account": "打开 @{name} 的管理界面",
"status.admin_domain": "打开 {domain} 的管理界面", "status.admin_domain": "打开 {domain} 的管理界面",
"status.admin_status": "在管理界面查看此嘟文", "status.admin_status": "在管理界面查看此嘟文",

View File

@@ -75,6 +75,8 @@ class StatusCacheHydrator
end end
end end
payload[:card][:missing_attribution] = status.preview_card.unverified_author_account_id == account_id if payload[:card]
# Nested statuses are more likely to have a stale cache # Nested statuses are more likely to have a stale cache
fill_status_stats(payload, status) if nested fill_status_stats(payload, status) if nested
end end

View File

@@ -33,6 +33,7 @@
# published_at :datetime # published_at :datetime
# image_description :string default(""), not null # image_description :string default(""), not null
# author_account_id :bigint(8) # author_account_id :bigint(8)
# unverified_author_account_id :bigint(8)
# #
class PreviewCard < ApplicationRecord class PreviewCard < ApplicationRecord
@@ -61,6 +62,7 @@ class PreviewCard < ApplicationRecord
has_one :trend, class_name: 'PreviewCardTrend', inverse_of: :preview_card, dependent: :destroy has_one :trend, class_name: 'PreviewCardTrend', inverse_of: :preview_card, dependent: :destroy
belongs_to :author_account, class_name: 'Account', optional: true belongs_to :author_account, class_name: 'Account', optional: true
belongs_to :unverified_author_account, class_name: 'Account', optional: true
has_attached_file :image, has_attached_file :image,
processors: [:lazy_thumbnail, :blurhash_transcoder], processors: [:lazy_thumbnail, :blurhash_transcoder],

View File

@@ -15,6 +15,8 @@ class REST::PreviewCardSerializer < ActiveModel::Serializer
has_many :authors, serializer: AuthorSerializer has_many :authors, serializer: AuthorSerializer
attribute :missing_attribution, if: :current_user?
def url def url
object.original_url.presence || object.url object.original_url.presence || object.url
end end
@@ -26,4 +28,12 @@ class REST::PreviewCardSerializer < ActiveModel::Serializer
def html def html
Sanitize.fragment(object.html, Sanitize::Config::MASTODON_OEMBED) Sanitize.fragment(object.html, Sanitize::Config::MASTODON_OEMBED)
end end
def missing_attribution
object.unverified_author_account_id.present? && object.unverified_author_account_id == current_user.account_id
end
def current_user?
!current_user.nil?
end
end end

View File

@@ -159,7 +159,14 @@ class FetchLinkCardService < BaseService
@card = PreviewCard.find_or_initialize_by(url: link_details_extractor.canonical_url) if link_details_extractor.canonical_url != @card.url @card = PreviewCard.find_or_initialize_by(url: link_details_extractor.canonical_url) if link_details_extractor.canonical_url != @card.url
@card.assign_attributes(link_details_extractor.to_preview_card_attributes) @card.assign_attributes(link_details_extractor.to_preview_card_attributes)
@card.author_account = linked_account if linked_account&.can_be_attributed_from?(domain) || provider&.trendable?
if linked_account.present?
# There is an overlap in the two conditions when `provider` is trendable. This is on purpose to give users
# a heads-up before we remove the `provider&.trendable?` condition.
@card.author_account = linked_account if linked_account.can_be_attributed_from?(domain) || provider&.trendable?
@card.unverified_author_account = linked_account if linked_account.local? && !linked_account.can_be_attributed_from?(domain)
end
@card.save_with_optional_image! unless @card.title.blank? && @card.html.blank? @card.save_with_optional_image! unless @card.title.blank? && @card.html.blank?
end end
end end

View File

@@ -6,16 +6,16 @@ class UnfollowService < BaseService
include Lockable include Lockable
# Unfollow and notify the remote user # Unfollow and notify the remote user
# @param [Account] source_account Where to unfollow from # @param [Account] follower Where to unfollow from
# @param [Account] target_account Which to unfollow # @param [Account] followee Which to unfollow
# @param [Hash] options # @param [Hash] options
# @option [Boolean] :skip_unmerge # @option [Boolean] :skip_unmerge
def call(source_account, target_account, options = {}) def call(follower, followee, options = {})
@source_account = source_account @follower = follower
@target_account = target_account @followee = followee
@options = options @options = options
with_redis_lock("relationship:#{[source_account.id, target_account.id].sort.join(':')}") do with_redis_lock("relationship:#{[follower.id, followee.id].sort.join(':')}") do
unfollow! || undo_follow_request! unfollow! || undo_follow_request!
end end
end end
@@ -23,19 +23,25 @@ class UnfollowService < BaseService
private private
def unfollow! def unfollow!
follow = Follow.find_by(account: @source_account, target_account: @target_account) follow = Follow.find_by(account: @follower, target_account: @followee)
return unless follow return unless follow
# List members are removed immediately with the follow relationship removal,
# so we need to fetch the list IDs first
list_ids = @follower.owned_lists.with_list_account(@followee).pluck(:list_id) unless @options[:skip_unmerge]
follow.destroy! follow.destroy!
create_notification(follow) if !@target_account.local? && @target_account.activitypub? if @followee.local? && @follower.remote? && @follower.activitypub?
create_reject_notification(follow) if @target_account.local? && !@source_account.local? && @source_account.activitypub? send_reject_follow(follow)
elsif @followee.remote? && @followee.activitypub?
send_undo_follow(follow)
end
unless @options[:skip_unmerge] unless @options[:skip_unmerge]
UnmergeWorker.perform_async(@target_account.id, @source_account.id, 'home') UnmergeWorker.perform_async(@followee.id, @follower.id, 'home')
UnmergeWorker.push_bulk(@source_account.owned_lists.with_list_account(@target_account).pluck(:list_id)) do |list_id| UnmergeWorker.push_bulk(list_ids) do |list_id|
[@target_account.id, list_id, 'list'] [@followee.id, list_id, 'list']
end end
end end
@@ -43,22 +49,21 @@ class UnfollowService < BaseService
end end
def undo_follow_request! def undo_follow_request!
follow_request = FollowRequest.find_by(account: @source_account, target_account: @target_account) follow_request = FollowRequest.find_by(account: @follower, target_account: @followee)
return unless follow_request return unless follow_request
follow_request.destroy! follow_request.destroy!
create_notification(follow_request) unless @target_account.local? send_undo_follow(follow_request) unless @followee.local?
follow_request follow_request
end end
def create_notification(follow) def send_undo_follow(follow)
ActivityPub::DeliveryWorker.perform_async(build_json(follow), follow.account_id, follow.target_account.inbox_url) ActivityPub::DeliveryWorker.perform_async(build_json(follow), follow.account_id, follow.target_account.inbox_url)
end end
def create_reject_notification(follow) def send_reject_follow(follow)
ActivityPub::DeliveryWorker.perform_async(build_reject_json(follow), follow.target_account_id, follow.account.inbox_url) ActivityPub::DeliveryWorker.perform_async(build_reject_json(follow), follow.target_account_id, follow.account.inbox_url)
end end

View File

@@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class UpdateAccountService < BaseService class UpdateAccountService < BaseService
PREVIEW_CARD_REATTRIBUTION_LIMIT = 1_000
def call(account, params, raise_error: false) def call(account, params, raise_error: false)
was_locked = account.locked was_locked = account.locked
update_method = raise_error ? :update! : :update update_method = raise_error ? :update! : :update
@@ -11,6 +13,7 @@ class UpdateAccountService < BaseService
authorize_all_follow_requests(account) if was_locked && !account.locked authorize_all_follow_requests(account) if was_locked && !account.locked
check_links(account) check_links(account)
process_hashtags(account) process_hashtags(account)
process_attribution_domains(account)
end end
rescue Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e rescue Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e
account.errors.add(:avatar, e.message) account.errors.add(:avatar, e.message)
@@ -36,4 +39,22 @@ class UpdateAccountService < BaseService
def process_hashtags(account) def process_hashtags(account)
account.tags_as_strings = Extractor.extract_hashtags(account.note) account.tags_as_strings = Extractor.extract_hashtags(account.note)
end end
def process_attribution_domains(account)
return unless account.attribute_previously_changed?(:attribution_domains)
# Go through the most recent cards, and do the rest in a background job
preview_cards = PreviewCard.where(unverified_author_account: account).reorder(id: :desc).limit(PREVIEW_CARD_REATTRIBUTION_LIMIT).to_a
should_queue_worker = preview_cards.size == PREVIEW_CARD_REATTRIBUTION_LIMIT
preview_cards = preview_cards.filter do |preview_card|
account.can_be_attributed_from?(preview_card.domain)
rescue Addressable::URI::InvalidURIError
false
end
PreviewCard.where(id: preview_cards.pluck(:id), unverified_author_account: account).update_all(author_account_id: account.id, unverified_author_account_id: nil)
UpdateLinkCardAttributionWorker.perform_async(account.id) if should_queue_worker
end
end end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
class UpdateLinkCardAttributionWorker
include Sidekiq::IterableJob
def build_enumerator(account_id, cursor:)
@account = Account.find_by(id: account_id)
return if @account.blank?
scope = PreviewCard.where(unverified_author_account: @account)
active_record_batches_enumerator(scope, cursor:)
end
def each_iteration(preview_cards, account_id)
preview_cards = preview_cards.filter do |preview_card|
@account.can_be_attributed_from?(preview_card.domain)
rescue Addressable::URI::InvalidURIError
false
end
PreviewCard.where(id: preview_cards.pluck(:id), unverified_author_account: @account).update_all(author_account_id: account_id, unverified_author_account_id: nil)
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddUnverifiedAuthorAccountIdToPreviewCards < ActiveRecord::Migration[8.1]
disable_ddl_transaction!
def change
safety_assured { add_reference :preview_cards, :unverified_author_account, null: true, foreign_key: { to_table: 'accounts', on_delete: :nullify }, index: false }
add_index :preview_cards, [:unverified_author_account_id, :id], algorithm: :concurrently, where: 'unverified_author_account_id IS NOT NULL'
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[8.0].define(version: 2026_02_17_154542) do ActiveRecord::Schema[8.0].define(version: 2026_03_03_144409) 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 "pg_catalog.plpgsql" enable_extension "pg_catalog.plpgsql"
@@ -964,7 +964,9 @@ ActiveRecord::Schema[8.0].define(version: 2026_02_17_154542) do
t.datetime "published_at" t.datetime "published_at"
t.string "image_description", default: "", null: false t.string "image_description", default: "", null: false
t.bigint "author_account_id" t.bigint "author_account_id"
t.bigint "unverified_author_account_id"
t.index ["author_account_id"], name: "index_preview_cards_on_author_account_id", where: "(author_account_id IS NOT NULL)" t.index ["author_account_id"], name: "index_preview_cards_on_author_account_id", where: "(author_account_id IS NOT NULL)"
t.index ["unverified_author_account_id", "id"], name: "index_preview_cards_on_unverified_author_account_id_and_id", where: "(unverified_author_account_id IS NOT NULL)"
t.index ["url"], name: "index_preview_cards_on_url", unique: true t.index ["url"], name: "index_preview_cards_on_url", unique: true
end end

View File

@@ -19,6 +19,18 @@ RSpec.describe StatusCacheHydrator do
end end
end end
context 'when handling a new status with a preview card with unverified account attribution' do
let(:preview_card) { Fabricate(:preview_card, unverified_author_account: account) }
before do
PreviewCardsStatus.create(status: status, preview_card: preview_card)
end
it 'renders the same attributes as a full render' do
expect(subject).to eql(compare_to_hash)
end
end
context 'when handling a new status with own poll' do context 'when handling a new status with own poll' do
let(:poll) { Fabricate(:poll, account: account) } let(:poll) { Fabricate(:poll, account: account) }
let(:status) { Fabricate(:status, poll: poll, account: account) } let(:status) { Fabricate(:status, poll: poll, account: account) }

View File

@@ -6,10 +6,16 @@ RSpec.describe REST::PreviewCardSerializer do
subject do subject do
serialized_record_json( serialized_record_json(
preview_card, preview_card,
described_class described_class,
options: {
scope: current_user,
scope_name: :current_user,
}
) )
end end
let(:current_user) { nil }
context 'when preview card does not have author data' do context 'when preview card does not have author data' do
let(:preview_card) { Fabricate.build :preview_card } let(:preview_card) { Fabricate.build :preview_card }

View File

@@ -5,54 +5,57 @@ require 'rails_helper'
RSpec.describe UnfollowService do RSpec.describe UnfollowService do
subject { described_class.new } subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') } let(:follower) { Fabricate(:account) }
let(:followee) { Fabricate(:account) }
describe 'local' do before do
let(:bob) { Fabricate(:account, username: 'bob') } follower.follow!(followee)
end
before { sender.follow!(bob) } shared_examples 'when the followee is in a list' do
let(:list) { Fabricate(:list, account: follower) }
it 'destroys the following relation' do before do
subject.call(sender, bob) list.accounts << followee
end
expect(sender) it 'schedules removal of posts from this user from the list' do
.to_not be_following(bob) expect { subject.call(follower, followee) }
.to enqueue_sidekiq_job(UnmergeWorker).with(followee.id, list.id, 'list')
end end
end end
describe 'remote ActivityPub', :inline_jobs do describe 'a local user unfollowing another local user' do
let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } it 'destroys the following relation and unmerge from home' do
expect { subject.call(follower, followee) }
before do .to change { follower.following?(followee) }.from(true).to(false)
sender.follow!(bob) .and enqueue_sidekiq_job(UnmergeWorker).with(followee.id, follower.id, 'home')
stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
end end
it 'destroys the following relation and sends unfollow activity' do it_behaves_like 'when the followee is in a list'
subject.call(sender, bob)
expect(sender)
.to_not be_following(bob)
expect(a_request(:post, 'http://example.com/inbox'))
.to have_been_made.once
end
end end
describe 'remote ActivityPub (reverse)', :inline_jobs do describe 'a local user unfollowing a remote ActivityPub user' do
let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } let(:followee) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
before do it 'destroys the following relation, unmerge from home and sends undo activity' do
bob.follow!(sender) expect { subject.call(follower, followee) }
stub_request(:post, 'http://example.com/inbox').to_return(status: 200) .to change { follower.following?(followee) }.from(true).to(false)
.and enqueue_sidekiq_job(UnmergeWorker).with(followee.id, follower.id, 'home')
.and enqueue_sidekiq_job(ActivityPub::DeliveryWorker).with(match_json_values(type: 'Undo'), follower.id, followee.inbox_url)
end end
it 'destroys the following relation and sends a reject activity' do it_behaves_like 'when the followee is in a list'
subject.call(bob, sender) end
expect(sender) describe 'a remote ActivityPub user unfollowing a local user' do
.to_not be_following(bob) let(:follower) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
expect(a_request(:post, 'http://example.com/inbox'))
.to have_been_made.once it 'destroys the following relation, unmerge from home and sends a reject activity' do
expect { subject.call(follower, followee) }
.to change { follower.following?(followee) }.from(true).to(false)
.and enqueue_sidekiq_job(UnmergeWorker).with(followee.id, follower.id, 'home')
.and enqueue_sidekiq_job(ActivityPub::DeliveryWorker).with(match_json_values(type: 'Reject'), followee.id, follower.inbox_url)
end end
end end
end end

View File

@@ -33,4 +33,18 @@ RSpec.describe UpdateAccountService do
expect(eve).to_not be_requested(account) expect(eve).to_not be_requested(account)
end end
end end
describe 'adding domains to attribution_domains' do
let(:account) { Fabricate(:account) }
let!(:preview_card) { Fabricate(:preview_card, url: 'https://writer.example.com/article', unverified_author_account: account, author_account: nil) }
let!(:unattributable_preview_card) { Fabricate(:preview_card, url: 'https://otherwriter.example.com/article', unverified_author_account: account, author_account: nil) }
let!(:unrelated_preview_card) { Fabricate(:preview_card) }
it 'reattributes expected preview cards' do
expect { subject.call(account, { attribution_domains: ['writer.example.com'] }) }
.to change { preview_card.reload.author_account }.from(nil).to(account)
.and not_change { unattributable_preview_card.reload.author_account }
.and(not_change { unrelated_preview_card.reload.author_account })
end
end
end end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe UpdateLinkCardAttributionWorker do
let(:worker) { described_class.new }
let(:account) { Fabricate(:account, attribution_domains: ['writer.example.com']) }
describe '#perform' do
let!(:preview_card) { Fabricate(:preview_card, url: 'https://writer.example.com/article', unverified_author_account: account, author_account: nil) }
let!(:unattributable_preview_card) { Fabricate(:preview_card, url: 'https://otherwriter.example.com/article', unverified_author_account: account, author_account: nil) }
let!(:unrelated_preview_card) { Fabricate(:preview_card) }
it 'reattributes expected preview cards' do
expect { worker.perform(account.id) }
.to change { preview_card.reload.author_account }.from(nil).to(account)
.and not_change { unattributable_preview_card.reload.author_account }
.and(not_change { unrelated_preview_card.reload.author_account })
end
end
end