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

This commit is contained in:
KMY 2023-12-14 09:21:21 +09:00
commit 08a2f557fe
61 changed files with 980 additions and 838 deletions

View file

@ -118,7 +118,7 @@ GEM
minitest (>= 5.1) minitest (>= 5.1)
mutex_m mutex_m
tzinfo (~> 2.0) tzinfo (~> 2.0)
addressable (2.8.5) addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0) aes_key_wrap (1.1.0)
android_key_attestation (0.3.0) android_key_attestation (0.3.0)
@ -220,9 +220,9 @@ GEM
database_cleaner-core (~> 2.0.0) database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1) database_cleaner-core (2.0.1)
date (3.3.4) date (3.3.4)
debug (1.8.0) debug (1.9.0)
irb (>= 1.5.0) irb (~> 1.10)
reline (>= 0.3.1) reline (>= 0.3.8)
debug_inspector (1.1.0) debug_inspector (1.1.0)
devise (4.9.3) devise (4.9.3)
bcrypt (~> 3.0) bcrypt (~> 3.0)
@ -326,7 +326,7 @@ GEM
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
globalid (1.2.1) globalid (1.2.1)
activesupport (>= 6.1) activesupport (>= 6.1)
haml (6.2.0) haml (6.3.0)
temple (>= 0.8.2) temple (>= 0.8.2)
thor thor
tilt tilt
@ -335,7 +335,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.51.0) haml_lint (0.52.0)
haml (>= 4.0) haml (>= 4.0)
parallel (~> 1.10) parallel (~> 1.10)
rainbow rainbow
@ -381,7 +381,7 @@ GEM
rdoc rdoc
reline (>= 0.3.8) reline (>= 0.3.8)
jmespath (1.6.2) jmespath (1.6.2)
json (2.7.0) json (2.7.1)
json-canonicalization (1.0.0) json-canonicalization (1.0.0)
json-jwt (1.15.3) json-jwt (1.15.3)
activesupport (>= 4.2) activesupport (>= 4.2)
@ -484,8 +484,8 @@ GEM
nokogiri (1.15.5) nokogiri (1.15.5)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
oj (3.16.2) oj (3.16.3)
bigdecimal (~> 3.1) bigdecimal (>= 3.0)
omniauth (2.1.1) omniauth (2.1.1)
hashie (>= 3.4.6) hashie (>= 3.4.6)
rack (>= 2.2.3) rack (>= 2.2.3)
@ -622,7 +622,7 @@ GEM
redis (>= 4) redis (>= 4)
redlock (1.3.2) redlock (1.3.2)
redis (>= 3.0.0, < 6.0) redis (>= 3.0.0, < 6.0)
regexp_parser (2.8.2) regexp_parser (2.8.3)
reline (0.4.1) reline (0.4.1)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.5.1) request_store (1.5.1)
@ -664,7 +664,7 @@ GEM
rspec-mocks (~> 3.0) rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8) sidekiq (>= 5, < 8)
rspec-support (3.12.1) rspec-support (3.12.1)
rubocop (1.58.0) rubocop (1.59.0)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (>= 3.17.0)
parallel (~> 1.10) parallel (~> 1.10)
@ -761,7 +761,7 @@ GEM
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
terrapin (0.6.0) terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
test-prof (1.3.0) test-prof (1.3.1)
thor (1.3.0) thor (1.3.0)
tilt (2.3.0) tilt (2.3.0)
timeout (0.4.1) timeout (0.4.1)

View file

@ -114,7 +114,7 @@ module ApplicationHelper
end end
def fa_icon(icon, attributes = {}) def fa_icon(icon, attributes = {})
class_names = attributes[:class]&.split(' ') || [] class_names = attributes[:class]&.split || []
class_names << 'fa' class_names << 'fa'
class_names += icon.split.map { |cl| "fa-#{cl}" } class_names += icon.split.map { |cl| "fa-#{cl}" }

View file

@ -18,6 +18,6 @@ export const notificationsUpdate = createAction(
playSound: boolean; playSound: boolean;
}) => ({ }) => ({
payload: args, payload: args,
meta: { playSound: playSound ? { sound: 'boop' } : undefined }, meta: { sound: playSound ? 'boop' : undefined },
}), }),
); );

View file

@ -21,6 +21,7 @@
"account.blocked": "Blocked", "account.blocked": "Blocked",
"account.browse_more_on_origin_server": "Browse more on the original profile", "account.browse_more_on_origin_server": "Browse more on the original profile",
"account.cancel_follow_request": "Cancel follow", "account.cancel_follow_request": "Cancel follow",
"account.copy": "Copy link to profile",
"account.direct": "Privately mention @{name}", "account.direct": "Privately mention @{name}",
"account.disable_notifications": "Stop notifying me when @{name} posts", "account.disable_notifications": "Stop notifying me when @{name} posts",
"account.domain_blocked": "Domain blocked", "account.domain_blocked": "Domain blocked",
@ -191,6 +192,7 @@
"conversation.mark_as_read": "Mark as read", "conversation.mark_as_read": "Mark as read",
"conversation.open": "View conversation", "conversation.open": "View conversation",
"conversation.with": "With {names}", "conversation.with": "With {names}",
"copy_icon_button.copied": "Copied to clipboard",
"copypaste.copied": "Copied", "copypaste.copied": "Copied",
"copypaste.copy_to_clipboard": "Copy to clipboard", "copypaste.copy_to_clipboard": "Copy to clipboard",
"directory.federated": "From known fediverse", "directory.federated": "From known fediverse",
@ -222,6 +224,7 @@
"emoji_button.search_results": "Search results", "emoji_button.search_results": "Search results",
"emoji_button.symbols": "Symbols", "emoji_button.symbols": "Symbols",
"emoji_button.travel": "Travel & Places", "emoji_button.travel": "Travel & Places",
"empty_column.account_hides_collections": "This user has chosen to not make this information available",
"empty_column.account_suspended": "Account suspended", "empty_column.account_suspended": "Account suspended",
"empty_column.account_timeline": "No posts here!", "empty_column.account_timeline": "No posts here!",
"empty_column.account_unavailable": "Profile unavailable", "empty_column.account_unavailable": "Profile unavailable",
@ -478,6 +481,8 @@
"onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", "onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Personalize your home feed", "onboarding.follows.title": "Personalize your home feed",
"onboarding.profile.discoverable": "Make my profile discoverable",
"onboarding.profile.discoverable_hint": "When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.",
"onboarding.profile.display_name": "Display name", "onboarding.profile.display_name": "Display name",
"onboarding.profile.display_name_hint": "Your full name or your fun name…", "onboarding.profile.display_name_hint": "Your full name or your fun name…",
"onboarding.profile.lead": "You can always complete this later in the settings, where even more customisation options are available.", "onboarding.profile.lead": "You can always complete this later in the settings, where even more customisation options are available.",
@ -530,6 +535,7 @@
"privacy.unlisted.short": "Unlisted", "privacy.unlisted.short": "Unlisted",
"privacy_policy.last_updated": "Last updated {date}", "privacy_policy.last_updated": "Last updated {date}",
"privacy_policy.title": "Privacy Policy", "privacy_policy.title": "Privacy Policy",
"recommended": "Recommended",
"refresh": "Refresh", "refresh": "Refresh",
"regeneration_indicator.label": "Loading…", "regeneration_indicator.label": "Loading…",
"regeneration_indicator.sublabel": "Your home feed is being prepared!", "regeneration_indicator.sublabel": "Your home feed is being prepared!",
@ -600,6 +606,7 @@
"search.quick_action.status_search": "Posts matching {x}", "search.quick_action.status_search": "Posts matching {x}",
"search.search_or_paste": "Search or paste URL", "search.search_or_paste": "Search or paste URL",
"search_popout.full_text_search_disabled_message": "Unavailable on {domain}.", "search_popout.full_text_search_disabled_message": "Unavailable on {domain}.",
"search_popout.full_text_search_logged_out_message": "Only available when logged in.",
"search_popout.language_code": "ISO language code", "search_popout.language_code": "ISO language code",
"search_popout.options": "Search options", "search_popout.options": "Search options",
"search_popout.quick_actions": "Quick actions", "search_popout.quick_actions": "Quick actions",

View file

@ -21,6 +21,7 @@
"account.blocked": "Blokita", "account.blocked": "Blokita",
"account.browse_more_on_origin_server": "Foliumi pli ĉe la originala profilo", "account.browse_more_on_origin_server": "Foliumi pli ĉe la originala profilo",
"account.cancel_follow_request": "Nuligi peton por sekvado", "account.cancel_follow_request": "Nuligi peton por sekvado",
"account.copy": "Kopii ligilon al profilo",
"account.direct": "Private mencii @{name}", "account.direct": "Private mencii @{name}",
"account.disable_notifications": "Ne plu sciigi min, kiam @{name} mesaĝas", "account.disable_notifications": "Ne plu sciigi min, kiam @{name} mesaĝas",
"account.domain_blocked": "Domajno blokita", "account.domain_blocked": "Domajno blokita",
@ -191,6 +192,7 @@
"conversation.mark_as_read": "Marki legita", "conversation.mark_as_read": "Marki legita",
"conversation.open": "Vidi konversacion", "conversation.open": "Vidi konversacion",
"conversation.with": "Kun {names}", "conversation.with": "Kun {names}",
"copy_icon_button.copied": "Kopiis al kliptabulo",
"copypaste.copied": "Kopiita", "copypaste.copied": "Kopiita",
"copypaste.copy_to_clipboard": "Kopii al dosierujo", "copypaste.copy_to_clipboard": "Kopii al dosierujo",
"directory.federated": "El konata fediverso", "directory.federated": "El konata fediverso",
@ -202,7 +204,9 @@
"dismissable_banner.community_timeline": "Jen la plej novaj publikaj afiŝoj de uzantoj, kies kontojn gastigas {domain}.", "dismissable_banner.community_timeline": "Jen la plej novaj publikaj afiŝoj de uzantoj, kies kontojn gastigas {domain}.",
"dismissable_banner.dismiss": "Eksigi", "dismissable_banner.dismiss": "Eksigi",
"dismissable_banner.explore_links": "Tiuj novaĵoj estas aktuale priparolataj de uzantoj en tiu ĉi kaj aliaj serviloj, sur la malcentrigita reto.", "dismissable_banner.explore_links": "Tiuj novaĵoj estas aktuale priparolataj de uzantoj en tiu ĉi kaj aliaj serviloj, sur la malcentrigita reto.",
"dismissable_banner.explore_statuses": "Ĉi tioj estas afiŝoj de socia reto kiu populariĝas hodiau.",
"dismissable_banner.explore_tags": "Ĉi tiuj kradvostoj populariĝas en ĉi tiu kaj aliaj serviloj en la malcentraliza reto nun.", "dismissable_banner.explore_tags": "Ĉi tiuj kradvostoj populariĝas en ĉi tiu kaj aliaj serviloj en la malcentraliza reto nun.",
"dismissable_banner.public_timeline": "Ĉi tioj estas plej lastaj publikaj afiŝoj de personoj ĉe socia reto kiu personoj ĉe {domain} sekvas.",
"embed.instructions": "Enkorpigu ĉi tiun afiŝon en vian retejon per kopio de la suba kodo.", "embed.instructions": "Enkorpigu ĉi tiun afiŝon en vian retejon per kopio de la suba kodo.",
"embed.preview": "Ĝi aperos tiel:", "embed.preview": "Ĝi aperos tiel:",
"emoji_button.activity": "Agadoj", "emoji_button.activity": "Agadoj",
@ -220,6 +224,7 @@
"emoji_button.search_results": "Serĉaj rezultoj", "emoji_button.search_results": "Serĉaj rezultoj",
"emoji_button.symbols": "Simboloj", "emoji_button.symbols": "Simboloj",
"emoji_button.travel": "Vojaĝoj kaj lokoj", "emoji_button.travel": "Vojaĝoj kaj lokoj",
"empty_column.account_hides_collections": "Ĉi tiu uzanto elektis ne disponebligi ĉi tiu informon",
"empty_column.account_suspended": "Konto suspendita", "empty_column.account_suspended": "Konto suspendita",
"empty_column.account_timeline": "Neniu afiŝo ĉi tie!", "empty_column.account_timeline": "Neniu afiŝo ĉi tie!",
"empty_column.account_unavailable": "Profilo ne disponebla", "empty_column.account_unavailable": "Profilo ne disponebla",
@ -229,6 +234,8 @@
"empty_column.direct": "Vi ankoraŭ ne havas privatan mencion. Kiam vi sendos aŭ ricevos iun, tiu aperos ĉi tie.", "empty_column.direct": "Vi ankoraŭ ne havas privatan mencion. Kiam vi sendos aŭ ricevos iun, tiu aperos ĉi tie.",
"empty_column.domain_blocks": "Ankoraŭ neniu domajno estas blokita.", "empty_column.domain_blocks": "Ankoraŭ neniu domajno estas blokita.",
"empty_column.explore_statuses": "Nenio tendencas nun. Rekontrolu poste!", "empty_column.explore_statuses": "Nenio tendencas nun. Rekontrolu poste!",
"empty_column.favourited_statuses": "Vi ankoraŭ ne havas stelumitan afiŝon.",
"empty_column.favourites": "Ankoraŭ neniu stelumis tiun afiŝon.",
"empty_column.follow_requests": "Vi ne ankoraŭ havas iun peton de sekvado. Kiam vi ricevos unu, ĝi aperos ĉi tie.", "empty_column.follow_requests": "Vi ne ankoraŭ havas iun peton de sekvado. Kiam vi ricevos unu, ĝi aperos ĉi tie.",
"empty_column.followed_tags": "Vi ankoraŭ ne sekvas iujn kradvortojn. Kiam vi faras, ili aperos ĉi tie.", "empty_column.followed_tags": "Vi ankoraŭ ne sekvas iujn kradvortojn. Kiam vi faras, ili aperos ĉi tie.",
"empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.", "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
@ -292,19 +299,36 @@
"hashtag.column_settings.tag_mode.any": "Iu ajn", "hashtag.column_settings.tag_mode.any": "Iu ajn",
"hashtag.column_settings.tag_mode.none": "Neniu", "hashtag.column_settings.tag_mode.none": "Neniu",
"hashtag.column_settings.tag_toggle": "Aldoni pliajn etikedojn por ĉi tiu kolumno", "hashtag.column_settings.tag_toggle": "Aldoni pliajn etikedojn por ĉi tiu kolumno",
"hashtag.counter_by_accounts": "{count, plural,one {{counter} partoprenanto} other {{counter} partoprenantoj}}",
"hashtag.counter_by_uses": "{count, plural,one {{counter} afiŝo} other {{counter} afiŝoj}}",
"hashtag.counter_by_uses_today": "{count, plural,one {{counter} afiŝo} other {{counter} afiŝoj}} hodiau",
"hashtag.follow": "Sekvi la kradvorton", "hashtag.follow": "Sekvi la kradvorton",
"hashtag.unfollow": "Ne plu sekvi la kradvorton", "hashtag.unfollow": "Ne plu sekvi la kradvorton",
"hashtags.and_other": "…kaj {count, plural,other {# pli}}",
"home.actions.go_to_explore": "Vidi kio populariĝas",
"home.actions.go_to_suggestions": "Trovi homojn por sekvi", "home.actions.go_to_suggestions": "Trovi homojn por sekvi",
"home.column_settings.basic": "Bazaj agordoj", "home.column_settings.basic": "Bazaj agordoj",
"home.column_settings.show_reblogs": "Montri diskonigojn", "home.column_settings.show_reblogs": "Montri diskonigojn",
"home.column_settings.show_replies": "Montri respondojn", "home.column_settings.show_replies": "Montri respondojn",
"home.explore_prompt.body": "Via hejmafiŝaro havos miksitajn afiŝojn de kradvortoj kiujn vi elektis sekvi, personoj kiujn vi elektis sekvi, kaj afiŝoj kiujn ili suprenigis.",
"home.explore_prompt.title": "Ĉi tio estas via hejma paĝo en Mastodon.",
"home.hide_announcements": "Kaŝi la anoncojn", "home.hide_announcements": "Kaŝi la anoncojn",
"home.pending_critical_update.body": "Ĝisdatigu vian servilon de Mastodon kiel eble plej baldau!",
"home.pending_critical_update.link": "Vidi ĝisdatigojn",
"home.pending_critical_update.title": "Kritika sekurĝisdatigo estas disponebla!",
"home.show_announcements": "Montri anoncojn", "home.show_announcements": "Montri anoncojn",
"interaction_modal.description.favourite": "Per konto ĉe Mastodon, vi povas stelumiti ĉi tiun afiŝon por sciigi la afiŝanton ke vi aprezigas ŝin kaj konservas por la estonteco.",
"interaction_modal.description.follow": "Kun konto ĉe Mastodon, vi povos sekvi {name} por vidi ties mesaĝojn en via hejmo.", "interaction_modal.description.follow": "Kun konto ĉe Mastodon, vi povos sekvi {name} por vidi ties mesaĝojn en via hejmo.",
"interaction_modal.description.reblog": "Kun konto ĉe Mastodon, vi povas diskonigi ĉi tiun afiŝon, por ke viaj propraj sekvantoj vidu ĝin.", "interaction_modal.description.reblog": "Kun konto ĉe Mastodon, vi povas diskonigi ĉi tiun afiŝon, por ke viaj propraj sekvantoj vidu ĝin.",
"interaction_modal.description.reply": "Kun konto ĉe Mastodon, vi povos respondi al ĉi tiu mesaĝo.", "interaction_modal.description.reply": "Kun konto ĉe Mastodon, vi povos respondi al ĉi tiu mesaĝo.",
"interaction_modal.login.action": "Prenu min hejmen",
"interaction_modal.login.prompt": "Domajno de via hejma servilo, ekz. mastodon.social",
"interaction_modal.no_account_yet": "Ĉu ne estas ĉe Mastodon?",
"interaction_modal.on_another_server": "En alia servilo", "interaction_modal.on_another_server": "En alia servilo",
"interaction_modal.on_this_server": "En ĉi tiu servilo", "interaction_modal.on_this_server": "En ĉi tiu servilo",
"interaction_modal.sign_in": "Vi ne estas ensalutita al ĉi tiu servilo.",
"interaction_modal.sign_in_hint": "Gvideto: Tio estas la retejo kie vi registris. Vi ankau povas tajpi vian plenan uzantonomon!",
"interaction_modal.title.favourite": "Stelumi la afiŝon de {name}",
"interaction_modal.title.follow": "Sekvi {name}", "interaction_modal.title.follow": "Sekvi {name}",
"interaction_modal.title.reblog": "Akceli la afiŝon de {name}", "interaction_modal.title.reblog": "Akceli la afiŝon de {name}",
"interaction_modal.title.reply": "Respondi al la afiŝo de {name}", "interaction_modal.title.reply": "Respondi al la afiŝo de {name}",
@ -320,6 +344,8 @@
"keyboard_shortcuts.direct": "por malfermi la kolumnon pri privataj mencioj", "keyboard_shortcuts.direct": "por malfermi la kolumnon pri privataj mencioj",
"keyboard_shortcuts.down": "iri suben en la listo", "keyboard_shortcuts.down": "iri suben en la listo",
"keyboard_shortcuts.enter": "malfermi mesaĝon", "keyboard_shortcuts.enter": "malfermi mesaĝon",
"keyboard_shortcuts.favourite": "Stelumi afiŝon",
"keyboard_shortcuts.favourites": "Malfermi la liston de la stelumoj",
"keyboard_shortcuts.federated": "Malfermi la frataran templinion", "keyboard_shortcuts.federated": "Malfermi la frataran templinion",
"keyboard_shortcuts.heading": "Klavaraj mallongigoj", "keyboard_shortcuts.heading": "Klavaraj mallongigoj",
"keyboard_shortcuts.home": "Malfermi la hejman templinion", "keyboard_shortcuts.home": "Malfermi la hejman templinion",
@ -366,6 +392,7 @@
"lists.search": "Serĉi inter la homoj, kiujn vi sekvas", "lists.search": "Serĉi inter la homoj, kiujn vi sekvas",
"lists.subheading": "Viaj listoj", "lists.subheading": "Viaj listoj",
"load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}", "load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}",
"loading_indicator.label": "Ŝargado…",
"media_gallery.toggle_visible": "{number, plural, one {Kaŝi la bildon} other {Kaŝi la bildojn}}", "media_gallery.toggle_visible": "{number, plural, one {Kaŝi la bildon} other {Kaŝi la bildojn}}",
"moved_to_account_banner.text": "Via konto {disabledAccount} estas malvalidigita ĉar vi movis ĝin al {movedToAccount}.", "moved_to_account_banner.text": "Via konto {disabledAccount} estas malvalidigita ĉar vi movis ĝin al {movedToAccount}.",
"mute_modal.duration": "Daŭro", "mute_modal.duration": "Daŭro",
@ -382,6 +409,7 @@
"navigation_bar.domain_blocks": "Blokitaj domajnoj", "navigation_bar.domain_blocks": "Blokitaj domajnoj",
"navigation_bar.edit_profile": "Redakti profilon", "navigation_bar.edit_profile": "Redakti profilon",
"navigation_bar.explore": "Esplori", "navigation_bar.explore": "Esplori",
"navigation_bar.favourites": "Stelumoj",
"navigation_bar.filters": "Silentigitaj vortoj", "navigation_bar.filters": "Silentigitaj vortoj",
"navigation_bar.follow_requests": "Petoj de sekvado", "navigation_bar.follow_requests": "Petoj de sekvado",
"navigation_bar.followed_tags": "Sekvataj kradvortoj", "navigation_bar.followed_tags": "Sekvataj kradvortoj",
@ -389,6 +417,7 @@
"navigation_bar.lists": "Listoj", "navigation_bar.lists": "Listoj",
"navigation_bar.logout": "Adiaŭi", "navigation_bar.logout": "Adiaŭi",
"navigation_bar.mutes": "Silentigitaj uzantoj", "navigation_bar.mutes": "Silentigitaj uzantoj",
"navigation_bar.opened_in_classic_interface": "Afiŝoj, kontoj, kaj aliaj specifaj paĝoj kiuj estas malfermititaj defaulta en la klasika reta interfaco.",
"navigation_bar.personal": "Persone", "navigation_bar.personal": "Persone",
"navigation_bar.pins": "Alpinglitaj mesaĝoj", "navigation_bar.pins": "Alpinglitaj mesaĝoj",
"navigation_bar.preferences": "Preferoj", "navigation_bar.preferences": "Preferoj",
@ -398,6 +427,7 @@
"not_signed_in_indicator.not_signed_in": "Necesas saluti por aliri tiun rimedon.", "not_signed_in_indicator.not_signed_in": "Necesas saluti por aliri tiun rimedon.",
"notification.admin.report": "{name} raportis {target}", "notification.admin.report": "{name} raportis {target}",
"notification.admin.sign_up": "{name} kreis konton", "notification.admin.sign_up": "{name} kreis konton",
"notification.favourite": "{name} stelumis vian afiŝon",
"notification.follow": "{name} eksekvis vin", "notification.follow": "{name} eksekvis vin",
"notification.follow_request": "{name} petis sekvi vin", "notification.follow_request": "{name} petis sekvi vin",
"notification.mention": "{name} menciis vin", "notification.mention": "{name} menciis vin",
@ -411,6 +441,7 @@
"notifications.column_settings.admin.report": "Novaj raportoj:", "notifications.column_settings.admin.report": "Novaj raportoj:",
"notifications.column_settings.admin.sign_up": "Novaj registriĝoj:", "notifications.column_settings.admin.sign_up": "Novaj registriĝoj:",
"notifications.column_settings.alert": "Sciigoj de la retumilo", "notifications.column_settings.alert": "Sciigoj de la retumilo",
"notifications.column_settings.favourite": "Stelumoj:",
"notifications.column_settings.filter_bar.advanced": "Montri ĉiujn kategoriojn", "notifications.column_settings.filter_bar.advanced": "Montri ĉiujn kategoriojn",
"notifications.column_settings.filter_bar.category": "Rapida filtra breto", "notifications.column_settings.filter_bar.category": "Rapida filtra breto",
"notifications.column_settings.filter_bar.show_bar": "Montri la breton de filtrilo", "notifications.column_settings.filter_bar.show_bar": "Montri la breton de filtrilo",
@ -428,6 +459,7 @@
"notifications.column_settings.update": "Redaktoj:", "notifications.column_settings.update": "Redaktoj:",
"notifications.filter.all": "Ĉiuj", "notifications.filter.all": "Ĉiuj",
"notifications.filter.boosts": "Diskonigoj", "notifications.filter.boosts": "Diskonigoj",
"notifications.filter.favourites": "Stelumoj",
"notifications.filter.follows": "Sekvoj", "notifications.filter.follows": "Sekvoj",
"notifications.filter.mentions": "Mencioj", "notifications.filter.mentions": "Mencioj",
"notifications.filter.polls": "Balotenketaj rezultoj", "notifications.filter.polls": "Balotenketaj rezultoj",
@ -441,14 +473,29 @@
"notifications_permission_banner.enable": "Ŝalti retumilajn sciigojn", "notifications_permission_banner.enable": "Ŝalti retumilajn sciigojn",
"notifications_permission_banner.how_to_control": "Por ricevi sciigojn kiam Mastodon ne estas malfermita, ebligu labortablajn sciigojn. Vi povas regi precize kiuj specoj de interagoj generas labortablajn sciigojn per la supra butono {icon} post kiam ili estas ebligitaj.", "notifications_permission_banner.how_to_control": "Por ricevi sciigojn kiam Mastodon ne estas malfermita, ebligu labortablajn sciigojn. Vi povas regi precize kiuj specoj de interagoj generas labortablajn sciigojn per la supra butono {icon} post kiam ili estas ebligitaj.",
"notifications_permission_banner.title": "Neniam preterlasas iun ajn", "notifications_permission_banner.title": "Neniam preterlasas iun ajn",
"onboarding.action.back": "Prenu min reen",
"onboarding.actions.back": "Prenu min reen",
"onboarding.actions.go_to_explore": "See what's trending", "onboarding.actions.go_to_explore": "See what's trending",
"onboarding.actions.go_to_home": "Go to your home feed", "onboarding.actions.go_to_home": "Go to your home feed",
"onboarding.compose.template": "Saluton #Mastodon!", "onboarding.compose.template": "Saluton #Mastodon!",
"onboarding.follows.empty": "Bedaŭrinde, neniu rezulto estas montrebla nuntempe. Vi povas provi serĉi aŭ foliumi la esploran paĝon por trovi kontojn por sekvi, aŭ retrovi baldaŭ.", "onboarding.follows.empty": "Bedaŭrinde, neniu rezulto estas montrebla nuntempe. Vi povas provi serĉi aŭ foliumi la esploran paĝon por trovi kontojn por sekvi, aŭ retrovi baldaŭ.",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon", "onboarding.follows.title": "Popular on Mastodon",
"onboarding.profile.discoverable": "Trovebligi mian profilon",
"onboarding.profile.discoverable_hint": "Kiam vi aliĝi al trovebleco ĉe Mastodon, viaj afiŝoj eble aperos en serĉaj rezultoj kaj populariĝoj, kaj via profilo eble estas sugestota al personoj kun similaj intereseoj al vi.",
"onboarding.profile.display_name": "Publika nomo",
"onboarding.profile.display_name_hint": "Via plena nomo aŭ via kromnomo…",
"onboarding.profile.lead": "Vi ĉiam povas plenigi ĉi tion poste en la agordoj, kie eĉ pli da personecigagordoj estas disponeblaj.",
"onboarding.profile.note": "Sinprezento",
"onboarding.profile.note_hint": "Vi povas @mencii aliajn homojn aŭ #kradvortojn…",
"onboarding.profile.save_and_continue": "Konservi kaj daŭrigi", "onboarding.profile.save_and_continue": "Konservi kaj daŭrigi",
"onboarding.profile.title": "Profila fikso",
"onboarding.profile.upload_avatar": "Alŝuti profilbildon",
"onboarding.profile.upload_header": "Alŝuti profilkapbildon",
"onboarding.share.lead": "Sciigi personojn pri kiel ili povas trovi vin ĉe Mastodon!",
"onboarding.share.message": "Mi estas {username} en #Mastodon! Sekvu min ĉe {url}", "onboarding.share.message": "Mi estas {username} en #Mastodon! Sekvu min ĉe {url}",
"onboarding.share.next_steps": "Eblaj malantauaj paŝoj:",
"onboarding.share.title": "Disvastigi vian profilon",
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
"onboarding.start.skip": "Want to skip right ahead?", "onboarding.start.skip": "Want to skip right ahead?",
"onboarding.start.title": "Vi atingas ĝin!", "onboarding.start.title": "Vi atingas ĝin!",
@ -460,6 +507,9 @@
"onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile", "onboarding.steps.share_profile.title": "Share your profile",
"onboarding.tips.2fa": "<strong>Ĉu vi scias?</strong> Vi povas sekurigi vian konton per efektivigi dufaktora autentigo en via kontoagordoj.",
"onboarding.tips.accounts_from_other_servers": "<strong>Ĉu vi scias?</strong> Ĉar Mastodon estas sencentra, kelkaj profiloj kiujn vi trovi estas gastigitaj ĉe aliaj serviloj kiuj ne estas via.",
"onboarding.tips.migration": "<strong>Ĉu vi scias?</strong> Se vi sentas ke {domain} ne estas bona servilelekto por vi en la estonteco, vi povas translokiĝi al alia servilo de Mastodon sen malgajni viajn sekvantojn.",
"password_confirmation.mismatching": "Pasvorto konfirmo ne kongruas", "password_confirmation.mismatching": "Pasvorto konfirmo ne kongruas",
"picture_in_picture.restore": "Remetu ĝin", "picture_in_picture.restore": "Remetu ĝin",
"poll.closed": "Finita", "poll.closed": "Finita",

View file

@ -606,6 +606,7 @@
"search.quick_action.status_search": "Sobivad postitused {x}", "search.quick_action.status_search": "Sobivad postitused {x}",
"search.search_or_paste": "Otsi või kleebi URL", "search.search_or_paste": "Otsi või kleebi URL",
"search_popout.full_text_search_disabled_message": "Pole saadaval kohas {domain}.", "search_popout.full_text_search_disabled_message": "Pole saadaval kohas {domain}.",
"search_popout.full_text_search_logged_out_message": "Saadaval vaid kui sisse logitud.",
"search_popout.language_code": "Keele ISO-kood", "search_popout.language_code": "Keele ISO-kood",
"search_popout.options": "Otsimisvalikud", "search_popout.options": "Otsimisvalikud",
"search_popout.quick_actions": "Kiirtegevused", "search_popout.quick_actions": "Kiirtegevused",

View file

@ -30,7 +30,7 @@
"account.blocked": "ブロック済み", "account.blocked": "ブロック済み",
"account.browse_more_on_origin_server": "リモートで表示", "account.browse_more_on_origin_server": "リモートで表示",
"account.cancel_follow_request": "フォローリクエストの取り消し", "account.cancel_follow_request": "フォローリクエストの取り消し",
"account.copy": "プロフィールのリンクをコピーして下さい", "account.copy": "プロフィールのリンクをコピー",
"account.direct": "@{name}さんに非公開でメンション", "account.direct": "@{name}さんに非公開でメンション",
"account.disable_notifications": "@{name}さんの投稿時の通知を停止", "account.disable_notifications": "@{name}さんの投稿時の通知を停止",
"account.domain_blocked": "ドメインブロック中", "account.domain_blocked": "ドメインブロック中",
@ -276,7 +276,7 @@
"conversation.mark_as_read": "既読にする", "conversation.mark_as_read": "既読にする",
"conversation.open": "会話を表示", "conversation.open": "会話を表示",
"conversation.with": "{names}", "conversation.with": "{names}",
"copy_icon_button.copied": "クリップボードにコピーされた", "copy_icon_button.copied": "コピーしました",
"copypaste.copied": "コピーしました", "copypaste.copied": "コピーしました",
"copypaste.copy_to_clipboard": "クリップボードにコピー", "copypaste.copy_to_clipboard": "クリップボードにコピー",
"directory.federated": "既知の連合より", "directory.federated": "既知の連合より",
@ -486,7 +486,7 @@
"lists.search": "フォローしている人の中から検索", "lists.search": "フォローしている人の中から検索",
"lists.subheading": "あなたのリスト", "lists.subheading": "あなたのリスト",
"load_pending": "{count}件の新着", "load_pending": "{count}件の新着",
"loading_indicator.label": "", "loading_indicator.label": "読み込み中…",
"media_gallery.toggle_visible": "{number, plural, one {画像を閉じる} other {画像を閉じる}}", "media_gallery.toggle_visible": "{number, plural, one {画像を閉じる} other {画像を閉じる}}",
"moved_to_account_banner.text": "あなたのアカウント『{disabledAccount}』は『{movedToAccount}』に移動したため現在無効になっています。", "moved_to_account_banner.text": "あなたのアカウント『{disabledAccount}』は『{movedToAccount}』に移動したため現在無効になっています。",
"mute_modal.duration": "ミュートする期間", "mute_modal.duration": "ミュートする期間",
@ -589,17 +589,17 @@
"onboarding.follows.empty": "表示できる結果はありません。検索やエクスプローラーを使ったり、ほかのアカウントをフォローしたり、後でもう一度試しください。", "onboarding.follows.empty": "表示できる結果はありません。検索やエクスプローラーを使ったり、ほかのアカウントをフォローしたり、後でもう一度試しください。",
"onboarding.follows.lead": "ホームタイムラインはMastodonの軸足となる場所です。たくさんのユーザーをフォローすることで、ホームタイムラインはよりにぎやかでおもしろいものになります。手はじめに、おすすめのアカウントから何人かフォローしてみましょう:", "onboarding.follows.lead": "ホームタイムラインはMastodonの軸足となる場所です。たくさんのユーザーをフォローすることで、ホームタイムラインはよりにぎやかでおもしろいものになります。手はじめに、おすすめのアカウントから何人かフォローしてみましょう:",
"onboarding.follows.title": "ホームタイムラインを埋める", "onboarding.follows.title": "ホームタイムラインを埋める",
"onboarding.profile.discoverable": "自分のプロフィールが発見できないようにする", "onboarding.profile.discoverable": "自分のプロフィールが見つけられるようにする",
"onboarding.profile.discoverable_hint": "マストドンの見つけやすくする機能が個人情報を利用することに承諾すると、あなたの投稿が検索結果やトレンドに表示されることがあります。また、あなたのプロフィールがあなたと似た興味関心を持つ人に提案されることがあります。", "onboarding.profile.discoverable_hint": "Mastodonの「見つける」機能にオプトインすると、あなたの投稿が検索結果やトレンドに表示されることがあります。また、あなたに似た関心を持つ人にプロフィールがおすすめされることがあります。",
"onboarding.profile.display_name": "表示名", "onboarding.profile.display_name": "表示名",
"onboarding.profile.display_name_hint": "あなたのフルネーム、または楽しい名前…", "onboarding.profile.display_name_hint": "フルネーム、あるいは面白い名前など",
"onboarding.profile.lead": "このことは後でいつでも設定から完了させることが出来ますし、設定では更に多くのカスタマイズが利用可能になっています。", "onboarding.profile.lead": "あとでいつでも修正できますし、設定画面にはこれ以外のカスタマイズ項目もあります。",
"onboarding.profile.note": "自己紹介", "onboarding.profile.note": "自己紹介",
"onboarding.profile.note_hint": "@を使用して他の人々にメンションをすることができます。また#でハッシュタグが使用できます", "onboarding.profile.note_hint": "ほかの人に @言及 したり、#ハッシュタグ を付けたりできます",
"onboarding.profile.save_and_continue": "保存してから続行して下さい", "onboarding.profile.save_and_continue": "保存して続ける",
"onboarding.profile.title": "プロフィールの設定", "onboarding.profile.title": "プロフィールの設定",
"onboarding.profile.upload_avatar": "プロフィール画像をアップロードしてください", "onboarding.profile.upload_avatar": "プロフィール画像をアップロード",
"onboarding.profile.upload_header": "プロフィールのヘッダー画像をアップロードして下さい", "onboarding.profile.upload_header": "プロフィールのヘッダー画像をアップロード",
"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": "次のステップに進む:",
@ -738,6 +738,7 @@
"searchability.unlisted.short": "フォロワーと反応者", "searchability.unlisted.short": "フォロワーと反応者",
"search_popout.domain": "ドメイン", "search_popout.domain": "ドメイン",
"search_popout.full_text_search_disabled_message": "{domain}では利用できません。", "search_popout.full_text_search_disabled_message": "{domain}では利用できません。",
"search_popout.full_text_search_logged_out_message": "ログイン時のみ利用できます。",
"search_popout.language_code": "ISO言語コード", "search_popout.language_code": "ISO言語コード",
"search_popout.options": "検索オプション", "search_popout.options": "検索オプション",
"search_popout.quick_actions": "クイック操作", "search_popout.quick_actions": "クイック操作",

View file

@ -239,7 +239,7 @@
"empty_column.follow_requests": "아직 팔로우 요청이 없습니다. 요청을 받았을 때 여기에 나타납니다.", "empty_column.follow_requests": "아직 팔로우 요청이 없습니다. 요청을 받았을 때 여기에 나타납니다.",
"empty_column.followed_tags": "아직 아무 해시태그도 팔로우하고 있지 않습니다. 해시태그를 팔로우하면, 여기에 표시됩니다.", "empty_column.followed_tags": "아직 아무 해시태그도 팔로우하고 있지 않습니다. 해시태그를 팔로우하면, 여기에 표시됩니다.",
"empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.", "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.",
"empty_column.home": "당신의 홈 타임라인은 비어있습니다! 더 많은 사람을 팔로우 하여 채워보세요. {suggestions}", "empty_column.home": "당신의 홈 타임라인은 비어있습니다! 더 많은 사람을 팔로우하여 채워보세요. {suggestions}",
"empty_column.list": "리스트에 아직 아무것도 없습니다. 리스트의 누군가가 게시물을 올리면 여기에 나타납니다.", "empty_column.list": "리스트에 아직 아무것도 없습니다. 리스트의 누군가가 게시물을 올리면 여기에 나타납니다.",
"empty_column.lists": "아직 리스트가 없습니다. 리스트를 만들면 여기에 나타납니다.", "empty_column.lists": "아직 리스트가 없습니다. 리스트를 만들면 여기에 나타납니다.",
"empty_column.mutes": "아직 아무도 뮤트하지 않았습니다.", "empty_column.mutes": "아직 아무도 뮤트하지 않았습니다.",

View file

@ -1,6 +1,6 @@
{ {
"about.contact": "Ratio:", "about.contact": "Ratio:",
"about.domain_blocks.no_reason_available": "ratio abdere est", "about.domain_blocks.no_reason_available": "Ratio abdere est",
"account.account_note_header": "Annotatio", "account.account_note_header": "Annotatio",
"account.badges.bot": "Robotum", "account.badges.bot": "Robotum",
"account.badges.group": "Congregatio", "account.badges.group": "Congregatio",
@ -49,7 +49,7 @@
"dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
"dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
"embed.instructions": "Embed this status on your website by copying the code below.", "embed.instructions": "Embed this status on your website by copying the code below.",
"emoji_button.food": "cibus et potus", "emoji_button.food": "Cibus et potus",
"emoji_button.people": "Homines", "emoji_button.people": "Homines",
"emoji_button.search": "Quaerere...", "emoji_button.search": "Quaerere...",
"empty_column.account_timeline": "Hic nulla contributa!", "empty_column.account_timeline": "Hic nulla contributa!",
@ -57,13 +57,13 @@
"empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}", "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
"empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
"explore.trending_statuses": "Contributa", "explore.trending_statuses": "Contributa",
"generic.saved": "servavit", "generic.saved": "Servavit",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column", "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"keyboard_shortcuts.back": "to navigate back", "keyboard_shortcuts.back": "Re navigare",
"keyboard_shortcuts.blocked": "to open blocked users list", "keyboard_shortcuts.blocked": "Aperire listam usorum obstructorum",
"keyboard_shortcuts.boost": "to boost", "keyboard_shortcuts.boost": "Inlustrare publicatio",
"keyboard_shortcuts.column": "to focus a status in one of the columns", "keyboard_shortcuts.column": "Columnam dirigere",
"keyboard_shortcuts.compose": "to focus the compose textarea", "keyboard_shortcuts.compose": "TextArea Compositi Attendere",
"keyboard_shortcuts.description": "Descriptio", "keyboard_shortcuts.description": "Descriptio",
"keyboard_shortcuts.direct": "to open direct messages column", "keyboard_shortcuts.direct": "to open direct messages column",
"keyboard_shortcuts.down": "to move down in the list", "keyboard_shortcuts.down": "to move down in the list",

View file

@ -1 +1,80 @@
{} {
"about.contact": "सम्पर्क:",
"about.disclaimer": "Mastodon नि:शुल्क, खुला स्रोत सफ्टवेयर, र Mastodon gGmbH को ट्रेडमार्क हो।",
"about.domain_blocks.no_reason_available": "कारण उपलब्ध छैन",
"about.domain_blocks.preamble": "Mastodon ले तपाइँलाई सामान्यतया फेडिभर्समा कुनै पनि अन्य सर्भरका सामग्री हेर्न र प्रयोगकर्ताहरूसँग अन्तरक्रिया गर्न दिन्छ। यी अपवादहरू हुन् जुन यस विशेष सर्भरमा बनाइएका छन्।",
"about.domain_blocks.silenced.title": "सीमित",
"about.domain_blocks.suspended.explanation": "यस सर्भरबाट कुनै पनि डेटा प्रशोधन, भण्डारण वा आदानप्रदान गरिने छैन, जसले यस सर्भरका प्रयोगकर्ताहरूसँग कुनै पनि अन्तरक्रिया वा सञ्चारलाई असम्भव बनाउँछ।",
"about.domain_blocks.suspended.title": "निलम्बित",
"about.not_available": "यो जानकारी यस सर्भरमा उपलब्ध गराइएको छैन।",
"about.powered_by": "{mastodon} द्वारा संचालित विकेन्द्रीकृत सामाजिक मिडिया",
"about.rules": "सर्भर नियमहरू",
"account.add_or_remove_from_list": "सूचीबाट थप्नुहोस् वा हटाउनुहोस्",
"account.badges.group": "समूह",
"account.block": "@{name} लाई ब्लक गर्नुहोस्",
"account.block_domain": "{domain} डोमेनलाई ब्लक गर्नुहोस्",
"account.block_short": "ब्लक",
"account.blocked": "ब्लक गरिएको",
"account.browse_more_on_origin_server": "मूल प्रोफाइलमा थप ब्राउज गर्नुहोस्",
"account.cancel_follow_request": "फलो अनुरोध रद्द गर्नुहोस",
"account.copy": "प्रोफाइलको लिङ्क प्रतिलिपि गर्नुहोस्",
"account.direct": "@{name} लाई निजी रूपमा उल्लेख गर्नुहोस्",
"account.disable_notifications": "@{name} ले पोस्ट गर्दा मलाई सूचित नगर्नुहोस्",
"account.domain_blocked": "डोमेन ब्लक गरिएको छ",
"account.edit_profile": "प्रोफाइल सम्पादन गर्नुहोस्",
"account.enable_notifications": "@{name} ले पोस्ट गर्दा मलाई सूचित गर्नुहोस्",
"account.endorse": "प्रोफाइलमा फिचर गर्नुहोस्",
"account.featured_tags.last_status_never": "कुनै पोस्ट छैन",
"account.follow": "फलो गर्नुहोस",
"account.followers.empty": "यस प्रयोगकर्तालाई अहिलेसम्म कसैले फलो गर्दैन।",
"account.follows.empty": "यो प्रयोगकर्ताले अहिलेसम्म कसैलाई फलो गरेको छैन।",
"account.go_to_profile": "प्रोफाइलमा जानुहोस्",
"account.hide_reblogs": "@{name} को बूस्टहरू लुकाउनुहोस्",
"account.link_verified_on": "यस लिङ्कको स्वामित्व {date} मा जाँच गरिएको थियो",
"account.media": "मिडिया",
"account.mention": "@{name} लाई उल्लेख गर्नुहोस्",
"account.no_bio": "कुनै विवरण प्रदान गरिएको छैन।",
"account.posts": "पोस्टहरू",
"account.requested": "स्वीकृतिको पर्खाइमा। फलो अनुरोध रद्द गर्न क्लिक गर्नुहोस्",
"account.requested_follow": "{name} ले तपाईंलाई फलो गर्न अनुरोध गर्नुभएको छ",
"account.share": "@{name} को प्रोफाइल सेयर गर्नुहोस्",
"account.show_reblogs": "@{name} को बूस्टहरू देखाउनुहोस्",
"account.statuses_counter": "{count, plural, one {{counter} पोस्ट} other {{counter} पोस्टहरू}}",
"account.unblock": "@{name} लाई अनब्लक गर्नुहोस्",
"account.unblock_domain": "{domain} डोमेनलाई अनब्लक गर्नुहोस्",
"account.unblock_short": "अनब्लक गर्नुहोस्",
"account.unendorse": "प्रोफाइलमा फिचर नगर्नुहोस्",
"account.unfollow": "अनफलो गर्नुहोस्",
"account_note.placeholder": "नोट लेख्न क्लिक गर्नुहोस्",
"admin.dashboard.retention.average": "औसत",
"admin.dashboard.retention.cohort_size": "नयाँ प्रयोगकर्ताहरू",
"alert.rate_limited.message": "कृपया {retry_time, time, medium} पछि पुन: प्रयास गर्नुहोस्।",
"alert.unexpected.message": "एउटा अनपेक्षित त्रुटि भयो।",
"bundle_column_error.retry": "पुन: प्रयास गर्नुहोस्",
"bundle_modal_error.close": "बन्द गर्नुहोस्",
"bundle_modal_error.message": "यो कम्पोनेन्ट लोड गर्दा केही गडबड भयो।",
"bundle_modal_error.retry": "Try again",
"closed_registrations.other_server_instructions": "Mastodon विकेन्द्रीकृत भएकोले, तपाइँ अर्को सर्भरमा खाता खोल्न सक्नुहुन्छ र पनि यो सर्भरसँग अन्तरक्रिया गर्न सक्नुहुन्छ।",
"closed_registrations_modal.description": "हाल {domain} मा खाता सिर्जना गर्न सम्भव छैन, तर कृपया ध्यान राख्नुहोस् कि तपाईंले Mastodon प्रयोग गर्नको लागि {domain} मा नै खाता खोल्न आवश्यक छैन।",
"closed_registrations_modal.find_another_server": "अर्को सर्भर खोज्नुहोस्",
"closed_registrations_modal.title": "Mastodon मा साइन अप गर्दै",
"column.blocks": "ब्लक गरिएको प्रयोगकर्ताहरु",
"column.directory": "प्रोफाइल ब्राउज गर्नुहोस्",
"column.domain_blocks": "ब्लक गरिएको डोमेन",
"column.follow_requests": "फलो अनुरोधहरू",
"column.lists": "सूचीहरू",
"column.notifications": "सूचनाहरू",
"column_header.hide_settings": "सेटिङ्हरू लुकाउनुहोस्",
"column_subheading.settings": "सेटिङहरू",
"compose.language.change": "भाषा परिवर्तन गर्नुहोस्",
"compose.language.search": "भाषाहरू खोज्नुहोस्...",
"compose_form.direct_message_warning_learn_more": "थप जान्नुहोस्",
"compose_form.poll.add_option": "विकल्प थप्नुहोस्",
"compose_form.poll.remove_option": "यो विकल्प हटाउनुहोस्",
"compose_form.publish_form": "नयाँ पोस्ट",
"compose_form.save_changes": "परिवर्तनहरू सेभ गर्नुहोस",
"compose_form.sensitive.hide": "{count, plural, one {संवेदनशील मिडियाको रूपमा चिन्ह लगाउनुहोस्} other {संवेदनशील मिडियाहरूको रूपमा चिन्ह लगाउनुहोस्}}",
"compose_form.sensitive.marked": "{count, plural, one {मिडियालाई संवेदनशील रूपमा चिन्ह लगाइएको छ} other {मिडियाहरूलाई संवेदनशील रूपमा चिन्ह लगाइएको छ}}",
"compose_form.sensitive.unmarked": "{count, plural, one {मिडियालाई संवेदनशील रूपमा चिन्ह लगाइएको छैन} other {मिडियाहरूलाई संवेदनशील रूपमा चिन्ह लगाइएको छैन}}",
"compose_form.spoiler_placeholder": "यहाँ आफ्नो चेतावनी लेख्नुहोस्"
}

View file

@ -224,6 +224,7 @@
"emoji_button.search_results": "Výsledky hľadania", "emoji_button.search_results": "Výsledky hľadania",
"emoji_button.symbols": "Symboly", "emoji_button.symbols": "Symboly",
"emoji_button.travel": "Cestovanie a miesta", "emoji_button.travel": "Cestovanie a miesta",
"empty_column.account_hides_collections": "Tento užívateľ si zvolil nesprístupniť túto informáciu",
"empty_column.account_suspended": "Účet bol pozastavený", "empty_column.account_suspended": "Účet bol pozastavený",
"empty_column.account_timeline": "Nie sú tu žiadne príspevky!", "empty_column.account_timeline": "Nie sú tu žiadne príspevky!",
"empty_column.account_unavailable": "Profil nedostupný", "empty_column.account_unavailable": "Profil nedostupný",

View file

@ -8,7 +8,7 @@
"about.domain_blocks.silenced.title": "已受限", "about.domain_blocks.silenced.title": "已受限",
"about.domain_blocks.suspended.explanation": "來自此伺服器的資料都不會被處理、儲存或交換,也無法與此伺服器上的使用者互動或交流。", "about.domain_blocks.suspended.explanation": "來自此伺服器的資料都不會被處理、儲存或交換,也無法與此伺服器上的使用者互動或交流。",
"about.domain_blocks.suspended.title": "已停權", "about.domain_blocks.suspended.title": "已停權",
"about.not_available": "無法於伺服器上使用此資訊。", "about.not_available": "無法於伺服器上使用此資訊。",
"about.powered_by": "由 {mastodon} 提供的去中心化社群媒體", "about.powered_by": "由 {mastodon} 提供的去中心化社群媒體",
"about.rules": "伺服器規則", "about.rules": "伺服器規則",
"account.account_note_header": "備註", "account.account_note_header": "備註",
@ -34,9 +34,9 @@
"account.follow": "跟隨", "account.follow": "跟隨",
"account.followers": "跟隨者", "account.followers": "跟隨者",
"account.followers.empty": "尚未有人跟隨這位使用者。", "account.followers.empty": "尚未有人跟隨這位使用者。",
"account.followers_counter": "被 {count, plural,one {{counter} 人}other {{counter} 人}}跟隨", "account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨",
"account.following": "跟隨中", "account.following": "跟隨中",
"account.following_counter": "正在跟隨 {count, plural, one {{counter} 人} other {{counter} 人}}", "account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}",
"account.follows.empty": "這位使用者尚未跟隨任何人。", "account.follows.empty": "這位使用者尚未跟隨任何人。",
"account.follows_you": "跟隨了您", "account.follows_you": "跟隨了您",
"account.go_to_profile": "前往個人檔案", "account.go_to_profile": "前往個人檔案",
@ -72,8 +72,8 @@
"account.unmute_notifications_short": "取消靜音推播通知", "account.unmute_notifications_short": "取消靜音推播通知",
"account.unmute_short": "解除靜音", "account.unmute_short": "解除靜音",
"account_note.placeholder": "按此新增備註", "account_note.placeholder": "按此新增備註",
"admin.dashboard.daily_retention": "註冊後使用者存留率(日)", "admin.dashboard.daily_retention": "註冊後使用者存留率(日)",
"admin.dashboard.monthly_retention": "註冊後使用者存留率(月)", "admin.dashboard.monthly_retention": "註冊後使用者存留率(月)",
"admin.dashboard.retention.average": "平均", "admin.dashboard.retention.average": "平均",
"admin.dashboard.retention.cohort": "註冊月份", "admin.dashboard.retention.cohort": "註冊月份",
"admin.dashboard.retention.cohort_size": "新使用者", "admin.dashboard.retention.cohort_size": "新使用者",
@ -105,7 +105,7 @@
"closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與這個伺服器互動。", "closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與這個伺服器互動。",
"closed_registrations_modal.description": "目前無法於 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon。", "closed_registrations_modal.description": "目前無法於 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon。",
"closed_registrations_modal.find_another_server": "尋找另一個伺服器", "closed_registrations_modal.find_another_server": "尋找另一個伺服器",
"closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架一個自己的伺服器!", "closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架一個自己的伺服器!",
"closed_registrations_modal.title": "註冊 Mastodon", "closed_registrations_modal.title": "註冊 Mastodon",
"column.about": "關於", "column.about": "關於",
"column.blocks": "已封鎖的使用者", "column.blocks": "已封鎖的使用者",
@ -155,7 +155,7 @@
"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": "標記媒體為敏感內容", "compose_form.sensitive.hide": "{count, plural, other {將媒體標記為敏感內容}}",
"compose_form.sensitive.marked": "此媒體被標記為敏感內容", "compose_form.sensitive.marked": "此媒體被標記為敏感內容",
"compose_form.sensitive.unmarked": "此媒體未被標記為敏感內容", "compose_form.sensitive.unmarked": "此媒體未被標記為敏感內容",
"compose_form.spoiler.marked": "移除內容警告", "compose_form.spoiler.marked": "移除內容警告",
@ -207,14 +207,14 @@
"dismissable_banner.explore_statuses": "這些於此伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。越多不同人轉嘟及最愛排名更高。", "dismissable_banner.explore_statuses": "這些於此伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。越多不同人轉嘟及最愛排名更高。",
"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": "自訂",
"emoji_button.flags": "旗幟", "emoji_button.flags": "旗幟",
"emoji_button.food": "食物 & 飲料", "emoji_button.food": "食物 & 飲料",
"emoji_button.label": "插入表情符號", "emoji_button.label": "插入表情圖案",
"emoji_button.nature": "自然", "emoji_button.nature": "自然",
"emoji_button.not_found": "啊就沒這表情符號吼!! (╯°□°)╯︵ ┻━┻", "emoji_button.not_found": "啊就沒這表情符號吼!! (╯°□°)╯︵ ┻━┻",
"emoji_button.objects": "物件", "emoji_button.objects": "物件",
@ -353,11 +353,11 @@
"keyboard_shortcuts.legend": "顯示此說明選單", "keyboard_shortcuts.legend": "顯示此說明選單",
"keyboard_shortcuts.local": "開啟本站時間軸", "keyboard_shortcuts.local": "開啟本站時間軸",
"keyboard_shortcuts.mention": "提及作者", "keyboard_shortcuts.mention": "提及作者",
"keyboard_shortcuts.muted": "開啟靜音使用者列表", "keyboard_shortcuts.muted": "開啟靜音使用者清單",
"keyboard_shortcuts.my_profile": "開啟個人檔案頁面", "keyboard_shortcuts.my_profile": "開啟個人檔案頁面",
"keyboard_shortcuts.notifications": "開啟通知欄", "keyboard_shortcuts.notifications": "開啟通知欄",
"keyboard_shortcuts.open_media": "開啟媒體", "keyboard_shortcuts.open_media": "開啟媒體",
"keyboard_shortcuts.pinned": "開啟釘選的嘟文列表", "keyboard_shortcuts.pinned": "開啟釘選的嘟文清單",
"keyboard_shortcuts.profile": "開啟作者的個人檔案頁面", "keyboard_shortcuts.profile": "開啟作者的個人檔案頁面",
"keyboard_shortcuts.reply": "回應嘟文", "keyboard_shortcuts.reply": "回應嘟文",
"keyboard_shortcuts.requests": "開啟跟隨請求列表", "keyboard_shortcuts.requests": "開啟跟隨請求列表",
@ -386,7 +386,7 @@
"lists.new.create": "新增列表", "lists.new.create": "新增列表",
"lists.new.title_placeholder": "新列表標題", "lists.new.title_placeholder": "新列表標題",
"lists.replies_policy.followed": "任何跟隨的使用者", "lists.replies_policy.followed": "任何跟隨的使用者",
"lists.replies_policy.list": "列表成員", "lists.replies_policy.list": "成員清單",
"lists.replies_policy.none": "沒有人", "lists.replies_policy.none": "沒有人",
"lists.replies_policy.title": "顯示回覆:", "lists.replies_policy.title": "顯示回覆:",
"lists.search": "搜尋您跟隨的使用者", "lists.search": "搜尋您跟隨的使用者",
@ -452,7 +452,7 @@
"notifications.column_settings.push": "推播通知", "notifications.column_settings.push": "推播通知",
"notifications.column_settings.reblog": "轉嘟:", "notifications.column_settings.reblog": "轉嘟:",
"notifications.column_settings.show": "於欄位中顯示", "notifications.column_settings.show": "於欄位中顯示",
"notifications.column_settings.sound": "播放音", "notifications.column_settings.sound": "播放",
"notifications.column_settings.status": "新嘟文:", "notifications.column_settings.status": "新嘟文:",
"notifications.column_settings.unread_notifications.category": "未讀通知", "notifications.column_settings.unread_notifications.category": "未讀通知",
"notifications.column_settings.unread_notifications.highlight": "突顯未讀通知", "notifications.column_settings.unread_notifications.highlight": "突顯未讀通知",
@ -477,7 +477,7 @@
"onboarding.actions.back": "返回", "onboarding.actions.back": "返回",
"onboarding.actions.go_to_explore": "看看發生什麼新鮮事", "onboarding.actions.go_to_explore": "看看發生什麼新鮮事",
"onboarding.actions.go_to_home": "前往您的首頁時間軸", "onboarding.actions.go_to_home": "前往您的首頁時間軸",
"onboarding.compose.template": "哈囉 #Mastodon", "onboarding.compose.template": "你好 #Mastodon",
"onboarding.follows.empty": "很遺憾,目前未能顯示任何結果。您可以嘗試使用搜尋、瀏覽探索頁面以找尋人們跟隨、或稍候再試。", "onboarding.follows.empty": "很遺憾,目前未能顯示任何結果。您可以嘗試使用搜尋、瀏覽探索頁面以找尋人們跟隨、或稍候再試。",
"onboarding.follows.lead": "您的首頁時間軸是 Mastodon 的核心體驗。若您跟隨更多人的話,它將會變得更活躍有趣。這些個人檔案也許是個好起點,您可以隨時取消跟隨他們!", "onboarding.follows.lead": "您的首頁時間軸是 Mastodon 的核心體驗。若您跟隨更多人的話,它將會變得更活躍有趣。這些個人檔案也許是個好起點,您可以隨時取消跟隨他們!",
"onboarding.follows.title": "客製化您的首頁時間軸", "onboarding.follows.title": "客製化您的首頁時間軸",
@ -540,7 +540,7 @@
"regeneration_indicator.label": "載入中…", "regeneration_indicator.label": "載入中…",
"regeneration_indicator.sublabel": "您的首頁時間軸正在準備中!", "regeneration_indicator.sublabel": "您的首頁時間軸正在準備中!",
"relative_time.days": "{number} 天", "relative_time.days": "{number} 天",
"relative_time.full.days": "{number, plural, one {# 天} other {# 天}}前", "relative_time.full.days": "{number, plural, other {# 天}}前",
"relative_time.full.hours": "{number, plural, one {# 小時} other {# 小時}}前", "relative_time.full.hours": "{number, plural, one {# 小時} other {# 小時}}前",
"relative_time.full.just_now": "剛剛", "relative_time.full.just_now": "剛剛",
"relative_time.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}前", "relative_time.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}前",
@ -620,7 +620,7 @@
"search_results.see_all": "檢視全部", "search_results.see_all": "檢視全部",
"search_results.statuses": "嘟文", "search_results.statuses": "嘟文",
"search_results.title": "搜尋:{q}", "search_results.title": "搜尋:{q}",
"server_banner.about_active_users": "最近三十日內使用此伺服器的人 (月活躍使用者)", "server_banner.about_active_users": "最近三十日內使用此伺服器的人(月活躍使用者)",
"server_banner.active_users": "活躍使用者", "server_banner.active_users": "活躍使用者",
"server_banner.administered_by": "管理者:", "server_banner.administered_by": "管理者:",
"server_banner.introduction": "{domain} 是由 {mastodon} 提供之去中心化社群網路一部分。", "server_banner.introduction": "{domain} 是由 {mastodon} 提供之去中心化社群網路一部分。",
@ -687,7 +687,7 @@
"status.translated_from_with": "透過 {provider} 翻譯 {lang}", "status.translated_from_with": "透過 {provider} 翻譯 {lang}",
"status.uncached_media_warning": "無法預覽", "status.uncached_media_warning": "無法預覽",
"status.unmute_conversation": "解除此對話的靜音", "status.unmute_conversation": "解除此對話的靜音",
"status.unpin": "個人檔案頁面取消釘選", "status.unpin": "個人檔案頁面取消釘選",
"subscribed_languages.lead": "僅選定語言的嘟文才會出現於您的首頁上,並於變更後列出時間軸。選取「無」以接收所有語言的嘟文。", "subscribed_languages.lead": "僅選定語言的嘟文才會出現於您的首頁上,並於變更後列出時間軸。選取「無」以接收所有語言的嘟文。",
"subscribed_languages.save": "儲存變更", "subscribed_languages.save": "儲存變更",
"subscribed_languages.target": "變更 {target} 的訂閱語言", "subscribed_languages.target": "變更 {target} 的訂閱語言",

View file

@ -4642,11 +4642,6 @@ a.status-card {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@supports (display: grid) {
// hack to fix Chrome <57
contain: strict;
}
& > span { & > span {
max-width: 500px; max-width: 500px;
} }

View file

@ -42,13 +42,13 @@ class InlineRenderer
private private
def preload_associations_for_status def preload_associations_for_status
ActiveRecord::Associations::Preloader.new(records: @object, associations: { ActiveRecord::Associations::Preloader.new(records: [@object], associations: {
active_mentions: :account, active_mentions: :account,
reblog: { reblog: {
active_mentions: :account, active_mentions: :account,
}, },
}) }).call
end end
def current_user def current_user

View file

@ -14,9 +14,8 @@ class Vacuum::PreviewCardsVacuum
private private
def vacuum_cached_images! def vacuum_cached_images!
preview_cards_past_retention_period.find_each do |preview_card| preview_cards_past_retention_period.find_in_batches do |preview_card|
preview_card.image.destroy AttachmentBatch.new(PreviewCard, preview_card).clear
preview_card.save
end end
end end

View file

@ -78,9 +78,9 @@ class Announcement < ApplicationRecord
else else
scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from announcement_reactions r where r.account_id = #{account.id} and r.announcement_id = announcement_reactions.announcement_id and r.name = announcement_reactions.name) as me") scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from announcement_reactions r where r.account_id = #{account.id} and r.announcement_id = announcement_reactions.announcement_id and r.name = announcement_reactions.name) as me")
end end
end end.to_a
ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji) ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji).call
records records
end end

View file

@ -147,7 +147,7 @@ module Account::Search
tsquery = generate_query_for_search(terms) tsquery = generate_query_for_search(terms)
find_by_sql([BASIC_SEARCH_SQL, { limit: limit, offset: offset, tsquery: tsquery }]).tap do |records| find_by_sql([BASIC_SEARCH_SQL, { limit: limit, offset: offset, tsquery: tsquery }]).tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat) ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
end end
end end
@ -160,7 +160,7 @@ module Account::Search
end end
find_by_sql([sql_template, { id: account.id, limit: limit, offset: offset, tsquery: tsquery }]).tap do |records| find_by_sql([sql_template, { id: account.id, limit: limit, offset: offset, tsquery: tsquery }]).tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat) ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
end end
end end

View file

@ -134,7 +134,7 @@ class Notification < ApplicationRecord
# Instead of using the usual `includes`, manually preload each type. # Instead of using the usual `includes`, manually preload each type.
# If polymorphic associations are loaded with the usual `includes`, other types of associations will be loaded more. # If polymorphic associations are loaded with the usual `includes`, other types of associations will be loaded more.
ActiveRecord::Associations::Preloader.new(records: grouped_notifications, associations: associations) ActiveRecord::Associations::Preloader.new(records: grouped_notifications, associations: associations).call
end end
unique_target_statuses = notifications.filter_map(&:target_status).uniq unique_target_statuses = notifications.filter_map(&:target_status).uniq

View file

@ -111,8 +111,8 @@ class InitialStateSerializer < ActiveModel::Serializer
ActiveRecord::Associations::Preloader.new( ActiveRecord::Associations::Preloader.new(
records: [object.current_account, object.admin, object.owner, object.disabled_account, object.moved_to_account].compact, records: [object.current_account, object.admin, object.owner, object.disabled_account, object.moved_to_account].compact,
associations: [:account_stat, :user, { moved_to_account: [:account_stat, :user] }] associations: [:account_stat, { user: :role, moved_to_account: [:account_stat, { user: :role }] }]
) ).call
store[object.current_account.id.to_s] = serialized_account(object.current_account) if object.current_account store[object.current_account.id.to_s] = serialized_account(object.current_account) if object.current_account
store[object.admin.id.to_s] = serialized_account(object.admin) if object.admin store[object.admin.id.to_s] = serialized_account(object.admin) if object.admin

View file

@ -242,7 +242,7 @@ class AccountSearchService < BaseService
records = query_builder.build.limit(limit_for_non_exact_results).offset(offset).objects.compact records = query_builder.build.limit(limit_for_non_exact_results).offset(offset).objects.compact
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat) ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
records records
rescue Faraday::ConnectionFailed, Parslet::ParseFailed rescue Faraday::ConnectionFailed, Parslet::ParseFailed

View file

@ -11,7 +11,7 @@ class BatchedRemoveStatusService < BaseService
ActiveRecord::Associations::Preloader.new( ActiveRecord::Associations::Preloader.new(
records: statuses, records: statuses,
associations: options[:skip_side_effects] ? :reblogs : [:account, :tags, reblogs: :account] associations: options[:skip_side_effects] ? :reblogs : [:account, :tags, reblogs: :account]
) ).call
statuses_and_reblogs = statuses.flat_map { |status| [status] + status.reblogs } statuses_and_reblogs = statuses.flat_map { |status| [status] + status.reblogs }
@ -23,7 +23,7 @@ class BatchedRemoveStatusService < BaseService
ActiveRecord::Associations::Preloader.new( ActiveRecord::Associations::Preloader.new(
records: statuses_with_account_conversations, records: statuses_with_account_conversations,
associations: [mentions: :account] associations: [mentions: :account]
) ).call
statuses_with_account_conversations.each(&:unlink_from_conversations!) statuses_with_account_conversations.each(&:unlink_from_conversations!)

View file

@ -91,6 +91,15 @@ class FanOutOnWriteService < BaseService
LocalNotificationWorker.push_bulk(mentions) do |mention| LocalNotificationWorker.push_bulk(mentions) do |mention|
[mention.account_id, mention.id, 'Mention', 'mention'] [mention.account_id, mention.id, 'Mention', 'mention']
end end
next unless update?
# This may result in duplicate update payloads, but this ensures clients
# are aware of edits to posts only appearing in mention notifications
# (e.g. private mentions or mentions by people they do not follow)
PushUpdateWorker.push_bulk(mentions.filter { |mention| subscribed_to_streaming_api?(mention.account_id) }) do |mention|
[mention.account_id, @status.id, "timeline:#{mention.account_id}:notifications", { 'update' => true }]
end
end end
end end
@ -217,4 +226,8 @@ class FanOutOnWriteService < BaseService
def broadcastable_unlisted_public? def broadcastable_unlisted_public?
@status.unlisted_visibility? && @status.compute_searchability == 'public' && !@status.reblog? && !@account.silenced? @status.unlisted_visibility? && @status.compute_searchability == 'public' && !@status.reblog? && !@account.silenced?
end end
def subscribed_to_streaming_api?(account_id)
redis.exists?("subscribed:timeline:#{account_id}") || redis.exists?("subscribed:timeline:#{account_id}:notifications")
end
end end

View file

@ -100,7 +100,7 @@ class FetchOEmbedService
end end
def validate(oembed) def validate(oembed)
oembed if oembed[:version].to_s == '1.0' && oembed[:type].present? oembed if oembed.present? && oembed[:version].to_s == '1.0' && oembed[:type].present?
end end
def html def html

View file

@ -63,13 +63,6 @@ module Mastodon
# Initialize configuration defaults for originally generated Rails version. # Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0 config.load_defaults 7.0
# TODO: Release a version which uses the 7.0 defaults as specified above,
# but preserves the 6.1 cache format as set below. In a subsequent change,
# remove this line setting to 6.1 cache format, and then release another version.
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activesupport-cache-serialization-format
# https://github.com/mastodon/mastodon/pull/24241#discussion_r1162890242
config.active_support.cache_format_version = 6.1
# Please, add to the `ignore` list any other `lib` subdirectories that do # Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded. # not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example. # Common ones are `templates`, `generators`, or `middleware`, for example.

View file

@ -1,9 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
# TODO: Remove after 4.2.0 # TODO: remove this file some time after 4.3.0
Rails.application.configure do
config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA1
end
Rails.application.config.after_initialize do Rails.application.config.after_initialize do
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
@ -12,9 +9,8 @@ Rails.application.config.after_initialize do
secret_key_base = Rails.application.secret_key_base secret_key_base = Rails.application.secret_key_base
# TODO: Switch to SHA1 after 4.2.0
key_generator = ActiveSupport::KeyGenerator.new( key_generator = ActiveSupport::KeyGenerator.new(
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA256 secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
) )
key_len = ActiveSupport::MessageEncryptor.key_len key_len = ActiveSupport::MessageEncryptor.key_len

View file

@ -6,7 +6,9 @@ hr:
expires_at: Krajnji rok expires_at: Krajnji rok
options: Opcije options: Opcije
user: user:
agreement: Ugovor o uslugama
email: E-mail adresa email: E-mail adresa
locale: Lokalitet
password: Lozinka password: Lozinka
user/account: user/account:
username: Korisničko ime username: Korisničko ime
@ -18,3 +20,16 @@ hr:
attributes: attributes:
username: username:
invalid: mora sadržavati samo slova, brojeve i _ invalid: mora sadržavati samo slova, brojeve i _
reserved: je rezervisano
admin/webhook:
attributes:
url:
invalid: nije validan URL
doorkeeper/application:
attributes:
website:
invalid: nije validan URL
import:
attributes:
data:
malformed: je neispravan

View file

@ -36,7 +36,7 @@ ru:
status: status:
attributes: attributes:
reblog: reblog:
taken: поста уже существует taken: пост уже существует
user: user:
attributes: attributes:
email: email:

View file

@ -28,7 +28,7 @@ zh-TW:
doorkeeper/application: doorkeeper/application:
attributes: attributes:
website: website:
invalid: 不是有效的 URL invalid: 不是有效的網址
import: import:
attributes: attributes:
data: data:

View file

@ -29,7 +29,7 @@ zh-TW:
edit: edit:
title: 編輯應用程式 title: 編輯應用程式
form: form:
error: 唉呦!請看看表單以排查錯誤 error: 糟糕!請檢查表單以排查錯誤
help: help:
native_redirect_uri: 請使用 %{native_redirect_uri} 作本站測試 native_redirect_uri: 請使用 %{native_redirect_uri} 作本站測試
redirect_uri: 每行輸入一個 URI redirect_uri: 每行輸入一個 URI

View file

@ -534,6 +534,7 @@ en-GB:
total_reported: Reports about them total_reported: Reports about them
total_storage: Media attachments total_storage: Media attachments
totals_time_period_hint_html: The totals displayed below include data for all time. totals_time_period_hint_html: The totals displayed below include data for all time.
unknown_instance: There is currently no record of this domain on this server.
invites: invites:
deactivate_all: Deactivate all deactivate_all: Deactivate all
filter: filter:
@ -610,6 +611,7 @@ en-GB:
created_at: Reported created_at: Reported
delete_and_resolve: Delete posts delete_and_resolve: Delete posts
forwarded: Forwarded forwarded: Forwarded
forwarded_replies_explanation: This report is from a remote user and about remote content. It has been forwarded to you because the reported content is in reply to one of your users.
forwarded_to: Forwarded to %{domain} forwarded_to: Forwarded to %{domain}
mark_as_resolved: Mark as resolved mark_as_resolved: Mark as resolved
mark_as_sensitive: Mark as sensitive mark_as_sensitive: Mark as sensitive
@ -1038,6 +1040,14 @@ en-GB:
hint_html: Just one more thing! We need to confirm you're a human (this is so we can keep the spam out!). Solve the CAPTCHA below and click "Continue". hint_html: Just one more thing! We need to confirm you're a human (this is so we can keep the spam out!). Solve the CAPTCHA below and click "Continue".
title: Security check title: Security check
confirmations: confirmations:
awaiting_review: Your e-mail address is confirmed! The %{domain} staff is now reviewing your registration. You will receive an e-mail if they approve your account!
awaiting_review_title: Your registration is being reviewed
clicking_this_link: clicking this link
login_link: log in
proceed_to_login_html: You can now proceed to %{login_link}.
redirect_to_app_html: You should have been redirected to the <strong>%{app_name}</strong> app. If that did not happen, try %{clicking_this_link} or manually return to the app.
registration_complete: Your registration on %{domain} is now complete!
welcome_title: Welcome, %{name}!
wrong_email_hint: If that e-mail address is not correct, you can change it in account settings. wrong_email_hint: If that e-mail address is not correct, you can change it in account settings.
delete_account: Delete account delete_account: Delete account
delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation. delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation.
@ -1099,6 +1109,7 @@ en-GB:
functional: Your account is fully operational. functional: Your account is fully operational.
pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved. pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved.
redirecting_to: Your account is inactive because it is currently redirecting to %{acct}. redirecting_to: Your account is inactive because it is currently redirecting to %{acct}.
self_destruct: As %{domain} is closing down, you will only get limited access to your account.
view_strikes: View past strikes against your account view_strikes: View past strikes against your account
too_fast: Form submitted too fast, try again. too_fast: Form submitted too fast, try again.
use_security_key: Use security key use_security_key: Use security key
@ -1356,6 +1367,7 @@ en-GB:
'86400': 1 day '86400': 1 day
expires_in_prompt: Never expires_in_prompt: Never
generate: Generate invite link generate: Generate invite link
invalid: This invite is not valid
invited_by: 'You were invited by:' invited_by: 'You were invited by:'
max_uses: max_uses:
one: 1 use one: 1 use
@ -1568,6 +1580,9 @@ en-GB:
over_daily_limit: You have exceeded the limit of %{limit} scheduled posts for today over_daily_limit: You have exceeded the limit of %{limit} scheduled posts for today
over_total_limit: You have exceeded the limit of %{limit} scheduled posts over_total_limit: You have exceeded the limit of %{limit} scheduled posts
too_soon: The scheduled date must be in the future too_soon: The scheduled date must be in the future
self_destruct:
lead_html: Unfortunately, <strong>%{domain}</strong> is permanently closing down. If you had an account there, you will not be able to continue using it, but you can still request a backup of your data.
title: This server is closing down
sessions: sessions:
activity: Last activity activity: Last activity
browser: Browser browser: Browser
@ -1736,6 +1751,7 @@ en-GB:
default: "%b %d, %Y, %H:%M" default: "%b %d, %Y, %H:%M"
month: "%b %Y" month: "%b %Y"
time: "%H:%M" time: "%H:%M"
with_time_zone: "%b %d, %Y, %H:%M %Z"
translation: translation:
errors: errors:
quota_exceeded: The server-wide usage quota for the translation service has been exceeded. quota_exceeded: The server-wide usage quota for the translation service has been exceeded.

View file

@ -534,6 +534,7 @@ et:
total_reported: Nende kohta teateid total_reported: Nende kohta teateid
total_storage: Lisatud meedia total_storage: Lisatud meedia
totals_time_period_hint_html: Allpool kuvatud summad sisaldavad andmed kogu aja kohta. totals_time_period_hint_html: Allpool kuvatud summad sisaldavad andmed kogu aja kohta.
unknown_instance: Hetkel pole selle domeeni jaoks siin serveris kirjet.
invites: invites:
deactivate_all: Peata kõik deactivate_all: Peata kõik
filter: filter:
@ -610,6 +611,7 @@ et:
created_at: Teavitatud created_at: Teavitatud
delete_and_resolve: Kustuta postitused delete_and_resolve: Kustuta postitused
forwarded: Edastatud forwarded: Edastatud
forwarded_replies_explanation: See aruanne pärineb kaugkasutajalt ja käsitleb kaugsisu. See on edastatud sulle, sest raporteeritud sisu on vastus ühele sinu kasutajale.
forwarded_to: Edastatud %{domain} domeeni forwarded_to: Edastatud %{domain} domeeni
mark_as_resolved: Märgi lahendatuks mark_as_resolved: Märgi lahendatuks
mark_as_sensitive: Märgi kui tundlik sisu mark_as_sensitive: Märgi kui tundlik sisu
@ -832,6 +834,20 @@ et:
system_checks: system_checks:
database_schema_check: database_schema_check:
message_html: On ootel andmebaasi migreerimisi. Rakenduse ootuspäraseks toimimiseks palun käivita need message_html: On ootel andmebaasi migreerimisi. Rakenduse ootuspäraseks toimimiseks palun käivita need
elasticsearch_health_red:
message_html: Elasticsearch klaster on ebaterve (punane staatus), otsingufunktsioonid ei ole saadaval
elasticsearch_health_yellow:
message_html: Elasticsearch klaster on ebaterve (kollane staatus), võiksid uurida põhjust
elasticsearch_index_mismatch:
message_html: Elasticsearchi indeksite kaardistused on vananenud. Palun käivita <code>tootctl search deploy --only=%{value}</code>
elasticsearch_preset:
action: Vaata dokumentatsiooni
message_html: Elasticsearchi klastris on rohkem kui üks sõlme, kuid Mastodon ei ole nende kasutamiseks konfigureeritud.
elasticsearch_preset_single_node:
action: Vaata dokumentatsiooni
message_html: Elasticsearchi klastris on ainult üks sõlm <code>ES_PRESET</code> peaks olema seatud väärtusele <code>single_node_cluster</code>.
elasticsearch_reset_chewy:
message_html: Elasticsearchi süsteemiindeks on seadistuse muutumise tõttu vananenud. Palun käivita <code>tootctl search deploy --reset-chewy</code> selle uuendamiseks.
elasticsearch_running_check: elasticsearch_running_check:
message_html: Elasticsearch ei vasta. Kontrolli, kas see töötab või keela täistekstiotsing message_html: Elasticsearch ei vasta. Kontrolli, kas see töötab või keela täistekstiotsing
elasticsearch_version_check: elasticsearch_version_check:
@ -1024,6 +1040,14 @@ et:
hint_html: Üks asi veel! Me peame veenduma, et oled inimene (et me saaksime spämmi väljaspoole jätta!). Lahenda allpool olev CAPTCHA ja klõpsa "Jätka". hint_html: Üks asi veel! Me peame veenduma, et oled inimene (et me saaksime spämmi väljaspoole jätta!). Lahenda allpool olev CAPTCHA ja klõpsa "Jätka".
title: Turvalisuse kontroll title: Turvalisuse kontroll
confirmations: confirmations:
awaiting_review: E-posti aadress on kinnitatud! %{domain} töötajad vaatavad praegu registreeringut läbi. Saad e-kirja, kui nad kiidavad konto heaks!
awaiting_review_title: Su registreeringut vaadatakse läbi
clicking_this_link: klõpsates seda linki
login_link: logi sisse
proceed_to_login_html: Saad nüüd jätkata valikuga %{login_link}.
redirect_to_app_html: Sind oleks pidanud suunatama rakendusse <strong>%{app_name}</strong>. Kui seda ei juhtunud, proovi %{clicking_this_link} või naase käsitsi rakendusse.
registration_complete: Sinu registreering domeenil %{domain} on nüüd valmis!
welcome_title: Tere tulemast, %{name}!
wrong_email_hint: Kui see e-postiaadress pole korrektne, saad seda kontosätetes muuta. wrong_email_hint: Kui see e-postiaadress pole korrektne, saad seda kontosätetes muuta.
delete_account: Konto kustutamine delete_account: Konto kustutamine
delete_account_html: Kui soovid oma konto kustutada, siis <a href="%{path}">jätka siit</a>. Pead kustutamise eraldi kinnitama. delete_account_html: Kui soovid oma konto kustutada, siis <a href="%{path}">jätka siit</a>. Pead kustutamise eraldi kinnitama.
@ -1085,6 +1109,7 @@ et:
functional: Konto on täies mahus kasutatav. functional: Konto on täies mahus kasutatav.
pending: Taotlus ootab ülevaatamist meie personali poolt. See võib võtta mõne aja. Kui taotlus on vastu võetud, saadetakse sulle e-kiri. pending: Taotlus ootab ülevaatamist meie personali poolt. See võib võtta mõne aja. Kui taotlus on vastu võetud, saadetakse sulle e-kiri.
redirecting_to: See konto pole aktiivne, sest on suunatud aadressile %{acct}. redirecting_to: See konto pole aktiivne, sest on suunatud aadressile %{acct}.
self_destruct: Kuna %{domain} on sulgemisel, saad oma kontole vaid piiratud ligipääsu.
view_strikes: Vaata enda eelnevaid juhtumeid view_strikes: Vaata enda eelnevaid juhtumeid
too_fast: Vorm esitatud liiga kiirelt, proovi uuesti. too_fast: Vorm esitatud liiga kiirelt, proovi uuesti.
use_security_key: Kasuta turvavõtit use_security_key: Kasuta turvavõtit
@ -1342,6 +1367,7 @@ et:
'86400': 1 päev '86400': 1 päev
expires_in_prompt: Mitte kunagi expires_in_prompt: Mitte kunagi
generate: Loo generate: Loo
invalid: See kutse pole kehtiv
invited_by: 'Sind kutsus:' invited_by: 'Sind kutsus:'
max_uses: max_uses:
one: 1 kasutus one: 1 kasutus
@ -1554,6 +1580,9 @@ et:
over_daily_limit: Lubatud ajastatud postituste arv %{limit} päevas on tänaseks ületatud over_daily_limit: Lubatud ajastatud postituste arv %{limit} päevas on tänaseks ületatud
over_total_limit: Oled jõudnud ajastatud postituste lubatud maksimumarvuni %{limit} over_total_limit: Oled jõudnud ajastatud postituste lubatud maksimumarvuni %{limit}
too_soon: Ajastatud kuupäev peab olema tukevikus too_soon: Ajastatud kuupäev peab olema tukevikus
self_destruct:
lead_html: Kahjuks suletakse <strong>%{domain}</strong> lõplikult. Kui sul oli seal konto, ei saa sa seda enam kasutada, kuid siiski võid taotleda oma andmete varukoopiat.
title: See server suletakse
sessions: sessions:
activity: Viimane aktiivsus activity: Viimane aktiivsus
browser: Veebilehitseja browser: Veebilehitseja

View file

@ -711,7 +711,7 @@ ja:
delete_and_resolve: 投稿を削除 delete_and_resolve: 投稿を削除
force_cw: 強制的にCWにする force_cw: 強制的にCWにする
forwarded: 転送済み forwarded: 転送済み
forwarded_replies_explanation: の報告はリモートユーザーからのものであり、またリモートコンテンツに関するものです。報告されたコンテンツがあなたのユーザーの内の一人に対するリプライの中にあるため、あなたに転送されました。 forwarded_replies_explanation: れはリモートユーザーによる、リモートコンテンツについての報告です。問題のコンテンツはあなたのサーバー利用者への返信なので、こちらにも転送されて来ました。
forwarded_to: "%{domain}に転送されました" forwarded_to: "%{domain}に転送されました"
mark_as_resolved: 解決済みとしてマーク mark_as_resolved: 解決済みとしてマーク
mark_as_sensitive: 閲覧注意にする mark_as_sensitive: 閲覧注意にする

View file

@ -16,7 +16,7 @@ zh-TW:
acct: 指定要移動至的帳號的「使用者名稱@網域名稱」 acct: 指定要移動至的帳號的「使用者名稱@網域名稱」
account_warning_preset: account_warning_preset:
text: 您可使用嘟文語法,例如網址、「#」標籤與提及功能 text: 您可使用嘟文語法,例如網址、「#」標籤與提及功能
title: 可選。不會向收件者顯示 title: 可選。不會向收件者顯示
admin_account_action: admin_account_action:
include_statuses: 使用者可看到導致檢舉或警告的嘟文 include_statuses: 使用者可看到導致檢舉或警告的嘟文
send_email_notification: 使用者將收到帳號發生之事情的解釋 send_email_notification: 使用者將收到帳號發生之事情的解釋

View file

@ -656,6 +656,10 @@ sk:
rules_check: rules_check:
action: Spravuj serverové pravidlá action: Spravuj serverové pravidlá
message_html: Neurčil/a si žiadne serverové pravidlá. message_html: Neurčil/a si žiadne serverové pravidlá.
software_version_critical_check:
action: Pozri dostupné aktualizácie
software_version_patch_check:
action: Pozri dostupné aktualizácie
upload_check_privacy_error: upload_check_privacy_error:
action: Pozri tu pre viac informácií action: Pozri tu pre viac informácií
upload_check_privacy_error_object_storage: upload_check_privacy_error_object_storage:

View file

@ -467,7 +467,7 @@ zh-TW:
other: 錯誤嘗試於 %{count} 天。 other: 錯誤嘗試於 %{count} 天。
no_failures_recorded: 報告中沒有錯誤。 no_failures_recorded: 報告中沒有錯誤。
title: 可用狀態 title: 可用狀態
warning: 上一次嘗試連線至伺服器失敗 warning: 上一次嘗試連線至伺服器失敗
back_to_all: 所有 back_to_all: 所有
back_to_limited: 受限制的 back_to_limited: 受限制的
back_to_warning: 警告 back_to_warning: 警告
@ -876,7 +876,7 @@ zh-TW:
publishers: publishers:
no_publisher_selected: 因未選取任何發行者,所以什麼事都沒發生 no_publisher_selected: 因未選取任何發行者,所以什麼事都沒發生
shared_by_over_week: shared_by_over_week:
other: 上週被 %{count} 使用者分享 other: 上週被 %{count} 使用者分享
title: 熱門連結 title: 熱門連結
usage_comparison: 於今日被 %{today} 人分享,相較於昨日 %{yesterday} 人 usage_comparison: 於今日被 %{today} 人分享,相較於昨日 %{yesterday} 人
not_allowed_to_trend: 不允許登上熱門 not_allowed_to_trend: 不允許登上熱門
@ -1273,7 +1273,7 @@ zh-TW:
other: 選取 %{count} 個符合您搜尋的項目。 other: 選取 %{count} 個符合您搜尋的項目。
today: 今天 today: 今天
validation_errors: validation_errors:
other: 恩...似乎不太對勁耶?請檢查以下 %{count} 項錯誤 other: 恩...似乎發生了點錯誤?請檢查以下 %{count} 項錯誤
imports: imports:
errors: errors:
empty: 空的 CSV 檔案 empty: 空的 CSV 檔案
@ -1796,7 +1796,7 @@ zh-TW:
welcome: welcome:
edit_profile_action: 設定個人檔案 edit_profile_action: 設定個人檔案
edit_profile_step: 您可以設定您的個人檔案,包括上傳大頭貼、變更顯示名稱等等。您也可以選擇於新的跟隨者跟隨前,先對他們進行審核。 edit_profile_step: 您可以設定您的個人檔案,包括上傳大頭貼、變更顯示名稱等等。您也可以選擇於新的跟隨者跟隨前,先對他們進行審核。
explanation: 下面是幾個小幫助,希望它們能幫到您 explanation: 以下是幾個小技巧,希望它們能幫到您
final_action: 開始嘟嘟 final_action: 開始嘟嘟
final_step: '開始嘟嘟吧!即使您現在沒有跟隨者,其他人仍然能於本站時間軸、主題標籤等地方,看到您的公開嘟文。試著用 #introductions 這個主題標籤介紹一下自己吧。' final_step: '開始嘟嘟吧!即使您現在沒有跟隨者,其他人仍然能於本站時間軸、主題標籤等地方,看到您的公開嘟文。試著用 #introductions 這個主題標籤介紹一下自己吧。'
full_handle: 您的完整帳號名稱 full_handle: 您的完整帳號名稱
@ -1805,7 +1805,7 @@ zh-TW:
title: "%{name} 誠摯歡迎您的加入!" title: "%{name} 誠摯歡迎您的加入!"
users: users:
follow_limit_reached: 您無法跟隨多於 %{limit} 個人 follow_limit_reached: 您無法跟隨多於 %{limit} 個人
go_to_sso_account_settings: 前往您的身分提供商 (identity provider) 之帳號設定 go_to_sso_account_settings: 前往您的身分識別提供者IdP之帳號設定
invalid_otp_token: 兩階段認證碼不正確 invalid_otp_token: 兩階段認證碼不正確
otp_lost_help_html: 如果您無法存取這兩者,您可以透過 %{email} 與我們聯繫 otp_lost_help_html: 如果您無法存取這兩者,您可以透過 %{email} 與我們聯繫
seamless_external_login: 由於您是由外部系統登入,所以不能設定密碼與電子郵件。 seamless_external_login: 由於您是由外部系統登入,所以不能設定密碼與電子郵件。

View file

@ -185,15 +185,15 @@ module Mastodon::CLI
end end
def schema_has_instances_view? def schema_has_instances_view?
ActiveRecord::Migrator.current_version >= 2020_12_06_004238 migrator_version >= 2020_12_06_004238
end end
def verify_schema_version! def verify_schema_version!
if ActiveRecord::Migrator.current_version < MIN_SUPPORTED_VERSION if migrator_version < MIN_SUPPORTED_VERSION
say 'Your version of the database schema is too old and is not supported by this script.', :red say 'Your version of the database schema is too old and is not supported by this script.', :red
say 'Please update to at least Mastodon 3.0.0 before running this script.', :red say 'Please update to at least Mastodon 3.0.0 before running this script.', :red
exit(1) exit(1)
elsif ActiveRecord::Migrator.current_version > MAX_SUPPORTED_VERSION elsif migrator_version > MAX_SUPPORTED_VERSION
say 'Your version of the database schema is more recent than this script, this may cause unexpected errors.', :yellow say 'Your version of the database schema is more recent than this script, this may cause unexpected errors.', :yellow
exit(1) unless yes?('Continue anyway? (Yes/No)') exit(1) unless yes?('Continue anyway? (Yes/No)')
end end
@ -228,7 +228,7 @@ module Mastodon::CLI
end end
say 'Restoring index_accounts_on_username_and_domain_lower…' say 'Restoring index_accounts_on_username_and_domain_lower…'
if ActiveRecord::Migrator.current_version < 2020_06_20_164023 if migrator_version < 2020_06_20_164023
ActiveRecord::Base.connection.add_index :accounts, 'lower (username), lower(domain)', name: 'index_accounts_on_username_and_domain_lower', unique: true ActiveRecord::Base.connection.add_index :accounts, 'lower (username), lower(domain)', name: 'index_accounts_on_username_and_domain_lower', unique: true
else else
ActiveRecord::Base.connection.add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true ActiveRecord::Base.connection.add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true
@ -238,7 +238,7 @@ module Mastodon::CLI
ActiveRecord::Base.connection.execute('REINDEX INDEX search_index;') ActiveRecord::Base.connection.execute('REINDEX INDEX search_index;')
ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_uri;') ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_uri;')
ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_url;') ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_url;')
ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_domain_and_id;') if ActiveRecord::Migrator.current_version >= 2023_05_24_190515 ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_domain_and_id;') if migrator_version >= 2023_05_24_190515
end end
def deduplicate_users! def deduplicate_users!
@ -269,15 +269,15 @@ module Mastodon::CLI
say 'Restoring users indexes…' say 'Restoring users indexes…'
ActiveRecord::Base.connection.add_index :users, ['confirmation_token'], name: 'index_users_on_confirmation_token', unique: true ActiveRecord::Base.connection.add_index :users, ['confirmation_token'], name: 'index_users_on_confirmation_token', unique: true
ActiveRecord::Base.connection.add_index :users, ['email'], name: 'index_users_on_email', unique: true ActiveRecord::Base.connection.add_index :users, ['email'], name: 'index_users_on_email', unique: true
ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if ActiveRecord::Migrator.current_version < 2022_01_18_183010 ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if migrator_version < 2022_01_18_183010
if ActiveRecord::Migrator.current_version < 2022_03_10_060641 if migrator_version < 2022_03_10_060641
ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true
else else
ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true, where: 'reset_password_token IS NOT NULL', opclass: :text_pattern_ops ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true, where: 'reset_password_token IS NOT NULL', opclass: :text_pattern_ops
end end
ActiveRecord::Base.connection.execute('REINDEX INDEX index_users_on_unconfirmed_email;') if ActiveRecord::Migrator.current_version >= 2023_07_02_151753 ActiveRecord::Base.connection.execute('REINDEX INDEX index_users_on_unconfirmed_email;') if migrator_version >= 2023_07_02_151753
end end
def deduplicate_users_process_confirmation_token def deduplicate_users_process_confirmation_token
@ -292,7 +292,7 @@ module Mastodon::CLI
end end
def deduplicate_users_process_remember_token def deduplicate_users_process_remember_token
if ActiveRecord::Migrator.current_version < 2022_01_18_183010 if migrator_version < 2022_01_18_183010
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE remember_token IS NOT NULL GROUP BY remember_token HAVING count(*) > 1").each do |row| ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE remember_token IS NOT NULL GROUP BY remember_token HAVING count(*) > 1").each do |row|
users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1) users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1)
say "Unsetting remember token for those accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow say "Unsetting remember token for those accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow
@ -371,7 +371,7 @@ module Mastodon::CLI
end end
say 'Restoring conversations indexes…' say 'Restoring conversations indexes…'
if ActiveRecord::Migrator.current_version < 2022_03_07_083603 if migrator_version < 2022_03_07_083603
ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true
else else
ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops
@ -488,7 +488,7 @@ module Mastodon::CLI
end end
say 'Restoring media_attachments indexes…' say 'Restoring media_attachments indexes…'
if ActiveRecord::Migrator.current_version < 2022_03_10_060626 if migrator_version < 2022_03_10_060626
ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true
else else
ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true, where: 'shortcode IS NOT NULL', opclass: :text_pattern_ops ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true, where: 'shortcode IS NOT NULL', opclass: :text_pattern_ops
@ -521,7 +521,7 @@ module Mastodon::CLI
end end
say 'Restoring statuses indexes…' say 'Restoring statuses indexes…'
if ActiveRecord::Migrator.current_version < 2022_03_10_060706 if migrator_version < 2022_03_10_060706
ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true
else else
ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops
@ -543,7 +543,7 @@ module Mastodon::CLI
end end
say 'Restoring tags indexes…' say 'Restoring tags indexes…'
if ActiveRecord::Migrator.current_version < 2021_04_21_121431 if migrator_version < 2021_04_21_121431
ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true
else else
ActiveRecord::Base.connection.execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)' ActiveRecord::Base.connection.execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)'
@ -707,12 +707,16 @@ module Mastodon::CLI
end end
end end
def migrator_version
ActiveRecord::Migrator.current_version
end
def find_duplicate_accounts def find_duplicate_accounts
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM accounts GROUP BY lower(username), COALESCE(lower(domain), '') HAVING count(*) > 1") ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM accounts GROUP BY lower(username), COALESCE(lower(domain), '') HAVING count(*) > 1")
end end
def remove_index_if_exists!(table, name) def remove_index_if_exists!(table, name)
ActiveRecord::Base.connection.remove_index(table, name: name) ActiveRecord::Base.connection.remove_index(table, name: name) if ActiveRecord::Base.connection.index_name_exists?(table, name)
rescue ArgumentError, ActiveRecord::StatementInvalid rescue ArgumentError, ActiveRecord::StatementInvalid
nil nil
end end

View file

@ -45,7 +45,13 @@ module Mastodon::CLI
pool = Concurrent::FixedThreadPool.new(options[:concurrency], max_queue: options[:concurrency] * 10) pool = Concurrent::FixedThreadPool.new(options[:concurrency], max_queue: options[:concurrency] * 10)
importers = indices.index_with { |index| "Importer::#{index.name}Importer".constantize.new(batch_size: options[:batch_size], executor: pool, full: options[:full], from: options[:from], to: options[:to]) } importers = indices.index_with { |index| "Importer::#{index.name}Importer".constantize.new(batch_size: options[:batch_size], executor: pool, full: options[:full], from: options[:from], to: options[:to]) }
progress = ProgressBar.create(total: nil, format: '%t%c/%u |%b%i| %e (%r docs/s)', autofinish: false) progress = ProgressBar.create(
{
total: nil,
format: '%t%c/%u |%b%i| %e (%r docs/s)',
autofinish: false,
}.merge(progress_output_options)
)
Chewy::Stash::Specification.reset! if options[:reset_chewy] Chewy::Stash::Specification.reset! if options[:reset_chewy]
@ -119,5 +125,9 @@ module Mastodon::CLI
say('Cannot run with this batch_size setting, must be at least 1', :red) say('Cannot run with this batch_size setting, must be at least 1', :red)
exit(1) exit(1)
end end
def progress_output_options
Rails.env.test? ? { output: ProgressBar::Outputs::Null } : {}
end
end end
end end

View file

@ -207,7 +207,7 @@
"prettier": "^3.0.0", "prettier": "^3.0.0",
"react-test-renderer": "^18.2.0", "react-test-renderer": "^18.2.0",
"stylelint": "^15.10.1", "stylelint": "^15.10.1",
"stylelint-config-standard-scss": "^11.0.0", "stylelint-config-standard-scss": "^12.0.0",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"webpack-dev-server": "^3.11.3", "webpack-dev-server": "^3.11.3",
"yargs": "^17.7.2" "yargs": "^17.7.2"

View file

@ -1,23 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Accounts::IdentityProofsController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
let(:account) { Fabricate(:account) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
end
end
end

View file

@ -1,25 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Accounts::ListsController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') }
let(:account) { Fabricate(:account) }
let(:list) { Fabricate(:list, account: user.account) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
user.account.follow!(account)
list.accounts << account
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id }
expect(response).to have_http_status(200)
end
end
end

View file

@ -1,23 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Accounts::LookupController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
let(:account) { Fabricate(:account) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #show' do
it 'returns http success' do
get :show, params: { account_id: account.id, acct: account.acct }
expect(response).to have_http_status(200)
end
end
end

View file

@ -1,40 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Api::V1::Accounts::PinsController do
let(:john) { Fabricate(:user) }
let(:kevin) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: john.id, scopes: 'write:accounts') }
before do
kevin.account.followers << john.account
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'POST #create' do
subject { post :create, params: { account_id: kevin.account.id } }
it 'creates account_pin', :aggregate_failures do
expect do
subject
end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(1)
expect(response).to have_http_status(200)
end
end
describe 'DELETE #destroy' do
subject { delete :destroy, params: { account_id: kevin.account.id } }
before do
Fabricate(:account_pin, account: john.account, target_account: kevin.account)
end
it 'destroys account_pin', :aggregate_failures do
expect do
subject
end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(-1)
expect(response).to have_http_status(200)
end
end
end

View file

@ -1,22 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Api::V1::Accounts::SearchController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #show' do
it 'returns http success' do
get :show, params: { q: 'query' }
expect(response).to have_http_status(200)
end
end
end

View file

@ -1,23 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::FeaturedTags::SuggestionsController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
let(:account) { Fabricate(:account) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
end
end
end

View file

@ -1,22 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V2::SuggestionsController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
it 'returns http success' do
get :index
expect(response).to have_http_status(200)
end
end
end

View file

@ -34,23 +34,21 @@ describe Mastodon::CLI::Accounts do
let(:action) { :create } let(:action) { :create }
shared_examples 'a new user with given email address and username' do shared_examples 'a new user with given email address and username' do
it 'creates a new user with the specified email address' do it 'creates user and accounts from options and displays success message' do
subject
expect(User.find_by(email: options[:email])).to be_present
end
it 'creates a new local account with the specified username' do
subject
expect(Account.find_local('tootctl_username')).to be_present
end
it 'returns "OK" and newly generated password' do
allow(SecureRandom).to receive(:hex).and_return('test_password') allow(SecureRandom).to receive(:hex).and_return('test_password')
expect { subject } expect { subject }
.to output_results("OK\nNew password: test_password") .to output_results('OK', 'New password: test_password')
expect(user_from_options).to be_present
expect(account_from_options).to be_present
end
def user_from_options
User.find_by(email: options[:email])
end
def account_from_options
Account.find_local('tootctl_username')
end end
end end
@ -79,7 +77,8 @@ describe Mastodon::CLI::Accounts do
it_behaves_like 'a new user with given email address and username' it_behaves_like 'a new user with given email address and username'
it 'creates a new user with confirmed status' do it 'creates a new user with confirmed status' do
subject expect { subject }
.to output_results('New password')
user = User.find_by(email: options[:email]) user = User.find_by(email: options[:email])
@ -97,7 +96,8 @@ describe Mastodon::CLI::Accounts do
it_behaves_like 'a new user with given email address and username' it_behaves_like 'a new user with given email address and username'
it 'creates a new user with approved status' do it 'creates a new user with approved status' do
subject expect { subject }
.to output_results('New password')
user = User.find_by(email: options[:email]) user = User.find_by(email: options[:email])
@ -113,7 +113,8 @@ describe Mastodon::CLI::Accounts do
it_behaves_like 'a new user with given email address and username' it_behaves_like 'a new user with given email address and username'
it 'creates a new user and assigns the specified role' do it 'creates a new user and assigns the specified role' do
subject expect { subject }
.to output_results('New password')
role = User.find_by(email: options[:email])&.role role = User.find_by(email: options[:email])&.role
@ -150,7 +151,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: 'tootctl_new@example.com', reattach: true, force: true } } let(:options) { { email: 'tootctl_new@example.com', reattach: true, force: true } }
it 'reattaches the account to the new user and deletes the previous user' do it 'reattaches the account to the new user and deletes the previous user' do
subject expect { subject }
.to output_results('New password')
user = Account.find_local('tootctl_username')&.user user = Account.find_local('tootctl_username')&.user
@ -199,14 +201,9 @@ describe Mastodon::CLI::Accounts do
let(:arguments) { [user.account.username] } let(:arguments) { [user.account.username] }
context 'when no option is provided' do context 'when no option is provided' do
it 'returns a successful message' do it 'returns a successful message and preserves user' do
expect { subject } expect { subject }
.to output_results('OK') .to output_results('OK')
end
it 'does not modify the user' do
subject
expect(user).to eq(user.reload) expect(user).to eq(user.reload)
end end
end end
@ -227,7 +224,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { role: default_role.name } } let(:options) { { role: default_role.name } }
it "updates the user's role to the specified role" do it "updates the user's role to the specified role" do
subject expect { subject }
.to output_results('OK')
role = user.reload.role role = user.reload.role
@ -242,7 +240,8 @@ describe Mastodon::CLI::Accounts do
let(:user) { Fabricate(:user, role: role) } let(:user) { Fabricate(:user, role: role) }
it "removes the user's role successfully" do it "removes the user's role successfully" do
subject expect { subject }
.to output_results('OK')
role = user.reload.role role = user.reload.role
@ -255,13 +254,15 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: 'new_email@email.com' } } let(:options) { { email: 'new_email@email.com' } }
it "sets the user's unconfirmed email to the provided email address" do it "sets the user's unconfirmed email to the provided email address" do
subject expect { subject }
.to output_results('OK')
expect(user.reload.unconfirmed_email).to eq(options[:email]) expect(user.reload.unconfirmed_email).to eq(options[:email])
end end
it "does not update the user's original email address" do it "does not update the user's original email address" do
subject expect { subject }
.to output_results('OK')
expect(user.reload.email).to eq('old_email@email.com') expect(user.reload.email).to eq('old_email@email.com')
end end
@ -271,13 +272,15 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: 'new_email@email.com', confirm: true } } let(:options) { { email: 'new_email@email.com', confirm: true } }
it "updates the user's email address to the provided email" do it "updates the user's email address to the provided email" do
subject expect { subject }
.to output_results('OK')
expect(user.reload.email).to eq(options[:email]) expect(user.reload.email).to eq(options[:email])
end end
it "sets the user's email address as confirmed" do it "sets the user's email address as confirmed" do
subject expect { subject }
.to output_results('OK')
expect(user.reload.confirmed?).to be(true) expect(user.reload.confirmed?).to be(true)
end end
@ -289,7 +292,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { confirm: true } } let(:options) { { confirm: true } }
it "confirms the user's email address" do it "confirms the user's email address" do
subject expect { subject }
.to output_results('OK')
expect(user.reload.confirmed?).to be(true) expect(user.reload.confirmed?).to be(true)
end end
@ -304,7 +308,9 @@ describe Mastodon::CLI::Accounts do
end end
it 'approves the user' do it 'approves the user' do
expect { subject }.to change { user.reload.approved }.from(false).to(true) expect { subject }
.to output_results('OK')
.and change { user.reload.approved }.from(false).to(true)
end end
end end
@ -313,7 +319,9 @@ describe Mastodon::CLI::Accounts do
let(:options) { { disable: true } } let(:options) { { disable: true } }
it 'disables the user' do it 'disables the user' do
expect { subject }.to change { user.reload.disabled }.from(false).to(true) expect { subject }
.to output_results('OK')
.and change { user.reload.disabled }.from(false).to(true)
end end
end end
@ -322,7 +330,9 @@ describe Mastodon::CLI::Accounts do
let(:options) { { enable: true } } let(:options) { { enable: true } }
it 'enables the user' do it 'enables the user' do
expect { subject }.to change { user.reload.disabled }.from(true).to(false) expect { subject }
.to output_results('OK')
.and change { user.reload.disabled }.from(true).to(false)
end end
end end
@ -342,7 +352,9 @@ describe Mastodon::CLI::Accounts do
let(:options) { { disable_2fa: true } } let(:options) { { disable_2fa: true } }
it 'disables the two-factor authentication for the user' do it 'disables the two-factor authentication for the user' do
expect { subject }.to change { user.reload.otp_required_for_login }.from(true).to(false) expect { subject }
.to output_results('OK')
.and change { user.reload.otp_required_for_login }.from(true).to(false)
end end
end end
@ -392,7 +404,8 @@ describe Mastodon::CLI::Accounts do
let(:arguments) { [account.username] } let(:arguments) { [account.username] }
it 'deletes the specified user successfully' do it 'deletes the specified user successfully' do
subject expect { subject }
.to output_results('Deleting')
expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
end end
@ -400,15 +413,10 @@ describe Mastodon::CLI::Accounts do
context 'with --dry-run option' do context 'with --dry-run option' do
let(:options) { { dry_run: true } } let(:options) { { dry_run: true } }
it 'does not delete the specified user' do it 'outputs a successful message in dry run mode and does not delete the user' do
subject
expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false)
end
it 'outputs a successful message in dry run mode' do
expect { subject } expect { subject }
.to output_results('OK (DRY RUN)') .to output_results('OK (DRY RUN)')
expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false)
end end
end end
@ -427,7 +435,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: account.user.email } } let(:options) { { email: account.user.email } }
it 'deletes the specified user successfully' do it 'deletes the specified user successfully' do
subject expect { subject }
.to output_results('Deleting')
expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
end end
@ -435,15 +444,12 @@ describe Mastodon::CLI::Accounts do
context 'with --dry-run option' do context 'with --dry-run option' do
let(:options) { { email: account.user.email, dry_run: true } } let(:options) { { email: account.user.email, dry_run: true } }
it 'does not delete the user' do it 'outputs a successful message in dry run mode and does not delete the user' do
subject
expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false)
end
it 'outputs a successful message in dry run mode' do
expect { subject } expect { subject }
.to output_results('OK (DRY RUN)') .to output_results('OK (DRY RUN)')
expect(delete_account_service)
.to_not have_received(:call)
.with(account, reserve_email: false)
end end
end end
@ -472,7 +478,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { all: true } } let(:options) { { all: true } }
it 'approves all pending registrations' do it 'approves all pending registrations' do
subject expect { subject }
.to output_results('OK')
expect(User.pluck(:approved).all?(true)).to be(true) expect(User.pluck(:approved).all?(true)).to be(true)
end end
@ -482,20 +489,20 @@ describe Mastodon::CLI::Accounts do
context 'when the number is positive' do context 'when the number is positive' do
let(:options) { { number: 2 } } let(:options) { { number: 2 } }
it 'approves the earliest n pending registrations' do it 'approves the earliest n pending registrations but not the remaining ones' do
subject expect { subject }
.to output_results('OK')
n_earliest_pending_registrations = User.order(created_at: :asc).first(options[:number])
expect(n_earliest_pending_registrations.all?(&:approved?)).to be(true) expect(n_earliest_pending_registrations.all?(&:approved?)).to be(true)
expect(pending_registrations.all?(&:approved?)).to be(false)
end end
it 'does not approve the remaining pending registrations' do def n_earliest_pending_registrations
subject User.order(created_at: :asc).first(options[:number])
end
pending_registrations = User.order(created_at: :asc).last(total_users - options[:number]) def pending_registrations
User.order(created_at: :asc).last(total_users - options[:number])
expect(pending_registrations.all?(&:approved?)).to be(false)
end end
end end
@ -512,15 +519,10 @@ describe Mastodon::CLI::Accounts do
context 'when the given number is greater than the number of users' do context 'when the given number is greater than the number of users' do
let(:options) { { number: total_users * 2 } } let(:options) { { number: total_users * 2 } }
it 'approves all users' do it 'approves all users and does not raise any error' do
subject
expect(User.pluck(:approved).all?(true)).to be(true)
end
it 'does not raise any error' do
expect { subject } expect { subject }
.to_not raise_error .to output_results('OK')
expect(User.pluck(:approved).all?(true)).to be(true)
end end
end end
end end
@ -531,7 +533,8 @@ describe Mastodon::CLI::Accounts do
let(:arguments) { [user.account.username] } let(:arguments) { [user.account.username] }
it 'approves the specified user successfully' do it 'approves the specified user successfully' do
subject expect { subject }
.to output_results('OK')
expect(user.reload.approved?).to be(true) expect(user.reload.approved?).to be(true)
end end
@ -575,18 +578,13 @@ describe Mastodon::CLI::Accounts do
stub_parallelize_with_progress! stub_parallelize_with_progress!
end end
it 'makes all local accounts follow the target account' do it 'displays a successful message and makes all local accounts follow the target account' do
subject expect { subject }
.to output_results("OK, followed target from #{Account.local.count} accounts")
expect(follow_service).to have_received(:call).with(follower_bob, target_account, any_args).once expect(follow_service).to have_received(:call).with(follower_bob, target_account, any_args).once
expect(follow_service).to have_received(:call).with(follower_rony, target_account, any_args).once expect(follow_service).to have_received(:call).with(follower_rony, target_account, any_args).once
expect(follow_service).to have_received(:call).with(follower_charles, target_account, any_args).once expect(follow_service).to have_received(:call).with(follower_charles, target_account, any_args).once
end end
it 'displays a successful message' do
expect { subject }
.to output_results("OK, followed target from #{Account.local.count} accounts")
end
end end
end end
@ -618,18 +616,13 @@ describe Mastodon::CLI::Accounts do
stub_parallelize_with_progress! stub_parallelize_with_progress!
end end
it 'makes all local accounts unfollow the target account' do it 'displays a successful message and makes all local accounts unfollow the target account' do
subject expect { subject }
.to output_results('OK, unfollowed target from 3 accounts')
expect(unfollow_service).to have_received(:call).with(follower_chris, target_account).once expect(unfollow_service).to have_received(:call).with(follower_chris, target_account).once
expect(unfollow_service).to have_received(:call).with(follower_rambo, target_account).once expect(unfollow_service).to have_received(:call).with(follower_rambo, target_account).once
expect(unfollow_service).to have_received(:call).with(follower_ana, target_account).once expect(unfollow_service).to have_received(:call).with(follower_ana, target_account).once
end end
it 'displays a successful message' do
expect { subject }
.to output_results('OK, unfollowed target from 3 accounts')
end
end end
end end
@ -651,22 +644,17 @@ describe Mastodon::CLI::Accounts do
let(:user) { account.user } let(:user) { account.user }
let(:arguments) { [account.username] } let(:arguments) { [account.username] }
it 'creates a new backup for the specified user' do before { allow(BackupWorker).to receive(:perform_async) }
expect { subject }.to change { user.backups.count }.by(1)
end
it 'creates a backup job' do
allow(BackupWorker).to receive(:perform_async)
subject
latest_backup = user.backups.last
it 'creates a new backup and backup job for the specified user and outputs success message' do
expect { subject }
.to change { user.backups.count }.by(1)
.and output_results('OK')
expect(BackupWorker).to have_received(:perform_async).with(latest_backup.id).once expect(BackupWorker).to have_received(:perform_async).with(latest_backup.id).once
end end
it 'displays a successful message' do def latest_backup
expect { subject } user.backups.last
.to output_results('OK')
end end
end end
end end
@ -691,7 +679,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_avatar!) allow(remote_account_example_com).to receive(:reset_avatar!)
allow(account_example_net).to receive(:reset_avatar!) allow(account_example_net).to receive(:reset_avatar!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(remote_account_example_com).to have_received(:reset_avatar!).once expect(remote_account_example_com).to have_received(:reset_avatar!).once
@ -701,7 +690,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh avatar for local accounts' do it 'does not refresh avatar for local accounts' do
allow(local_account).to receive(:reset_avatar!) allow(local_account).to receive(:reset_avatar!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_avatar!) expect(local_account).to_not have_received(:reset_avatar!)
@ -711,7 +701,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_header!) allow(remote_account_example_com).to receive(:reset_header!)
allow(account_example_net).to receive(:reset_header!) allow(account_example_net).to receive(:reset_header!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(remote_account_example_com).to have_received(:reset_header!).once expect(remote_account_example_com).to have_received(:reset_header!).once
@ -721,7 +712,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh the header for local accounts' do it 'does not refresh the header for local accounts' do
allow(local_account).to receive(:reset_header!) allow(local_account).to receive(:reset_header!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_header!) expect(local_account).to_not have_received(:reset_header!)
@ -742,7 +734,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_avatar!) allow(remote_account_example_com).to receive(:reset_avatar!)
allow(account_example_net).to receive(:reset_avatar!) allow(account_example_net).to receive(:reset_avatar!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_avatar!) expect(local_account).to_not have_received(:reset_avatar!)
@ -755,7 +748,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_header!) allow(remote_account_example_com).to receive(:reset_header!)
allow(account_example_net).to receive(:reset_header!) allow(account_example_net).to receive(:reset_header!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_header!) expect(local_account).to_not have_received(:reset_header!)
@ -788,7 +782,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_avatar!) allow(account_example_com_a).to receive(:reset_avatar!)
allow(account_example_com_b).to receive(:reset_avatar!) allow(account_example_com_b).to receive(:reset_avatar!)
cli.refresh(*arguments) expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_com_a).to have_received(:reset_avatar!).once expect(account_example_com_a).to have_received(:reset_avatar!).once
expect(account_example_com_b).to have_received(:reset_avatar!).once expect(account_example_com_b).to have_received(:reset_avatar!).once
@ -797,7 +792,8 @@ describe Mastodon::CLI::Accounts do
it 'does not reset the avatar for unspecified accounts' do it 'does not reset the avatar for unspecified accounts' do
allow(account_example_net).to receive(:reset_avatar!) allow(account_example_net).to receive(:reset_avatar!)
cli.refresh(*arguments) expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_net).to_not have_received(:reset_avatar!) expect(account_example_net).to_not have_received(:reset_avatar!)
end end
@ -806,7 +802,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_header!) allow(account_example_com_a).to receive(:reset_header!)
allow(account_example_com_b).to receive(:reset_header!) allow(account_example_com_b).to receive(:reset_header!)
cli.refresh(*arguments) expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_com_a).to have_received(:reset_header!).once expect(account_example_com_a).to have_received(:reset_header!).once
expect(account_example_com_b).to have_received(:reset_header!).once expect(account_example_com_b).to have_received(:reset_header!).once
@ -815,7 +812,8 @@ describe Mastodon::CLI::Accounts do
it 'does not reset the header for unspecified accounts' do it 'does not reset the header for unspecified accounts' do
allow(account_example_net).to receive(:reset_header!) allow(account_example_net).to receive(:reset_header!)
cli.refresh(*arguments) expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_net).to_not have_received(:reset_header!) expect(account_example_net).to_not have_received(:reset_header!)
end end
@ -848,7 +846,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_avatar!) allow(account_example_com_a).to receive(:reset_avatar!)
allow(account_example_com_b).to receive(:reset_avatar!) allow(account_example_com_b).to receive(:reset_avatar!)
cli.refresh(*arguments) expect { cli.refresh(*arguments) }
.to output_results('OK (DRY RUN)')
expect(account_example_com_a).to_not have_received(:reset_avatar!) expect(account_example_com_a).to_not have_received(:reset_avatar!)
expect(account_example_com_b).to_not have_received(:reset_avatar!) expect(account_example_com_b).to_not have_received(:reset_avatar!)
@ -858,7 +857,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_header!) allow(account_example_com_a).to receive(:reset_header!)
allow(account_example_com_b).to receive(:reset_header!) allow(account_example_com_b).to receive(:reset_header!)
cli.refresh(*arguments) expect { cli.refresh(*arguments) }
.to output_results('OK (DRY RUN)')
expect(account_example_com_a).to_not have_received(:reset_header!) expect(account_example_com_a).to_not have_received(:reset_header!)
expect(account_example_com_b).to_not have_received(:reset_header!) expect(account_example_com_b).to_not have_received(:reset_header!)
@ -884,7 +884,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_avatar!) allow(account_example_com_a).to receive(:reset_avatar!)
allow(account_example_com_b).to receive(:reset_avatar!) allow(account_example_com_b).to receive(:reset_avatar!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(account_example_com_a).to have_received(:reset_avatar!).once expect(account_example_com_a).to have_received(:reset_avatar!).once
@ -894,7 +895,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh the avatar for accounts outside specified domain' do it 'does not refresh the avatar for accounts outside specified domain' do
allow(account_example_net).to receive(:reset_avatar!) allow(account_example_net).to receive(:reset_avatar!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(account_example_net).to_not have_received(:reset_avatar!) expect(account_example_net).to_not have_received(:reset_avatar!)
@ -904,7 +906,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_header!) allow(account_example_com_a).to receive(:reset_header!)
allow(account_example_com_b).to receive(:reset_header!) allow(account_example_com_b).to receive(:reset_header!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope) expect(cli).to have_received(:parallelize_with_progress).with(scope)
expect(account_example_com_a).to have_received(:reset_header!).once expect(account_example_com_a).to have_received(:reset_header!).once
@ -914,7 +917,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh the header for accounts outside specified domain' do it 'does not refresh the header for accounts outside specified domain' do
allow(account_example_net).to receive(:reset_header!) allow(account_example_net).to receive(:reset_header!)
cli.refresh expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(account_example_net).to_not have_received(:reset_header!) expect(account_example_net).to_not have_received(:reset_header!)
@ -949,7 +953,8 @@ describe Mastodon::CLI::Accounts do
old_private_key = account.private_key old_private_key = account.private_key
old_public_key = account.public_key old_public_key = account.public_key
subject expect { subject }
.to output_results('OK')
account.reload account.reload
expect(account.private_key).to_not eq(old_private_key) expect(account.private_key).to_not eq(old_private_key)
@ -959,10 +964,12 @@ describe Mastodon::CLI::Accounts do
it 'broadcasts the new keys for the specified account' do it 'broadcasts the new keys for the specified account' do
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in) allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
subject expect { subject }
.to output_results('OK')
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
end end
end
context 'when the given username is not found' do context 'when the given username is not found' do
let(:arguments) { ['non_existent_username'] } let(:arguments) { ['non_existent_username'] }
@ -973,7 +980,6 @@ describe Mastodon::CLI::Accounts do
.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
end
context 'when --all option is provided' do context 'when --all option is provided' do
let!(:accounts) { Fabricate.times(2, :account) } let!(:accounts) { Fabricate.times(2, :account) }
@ -983,7 +989,8 @@ describe Mastodon::CLI::Accounts do
old_private_keys = accounts.map(&:private_key) old_private_keys = accounts.map(&:private_key)
old_public_keys = accounts.map(&:public_key) old_public_keys = accounts.map(&:public_key)
subject expect { subject }
.to output_results('rotated')
accounts.each(&:reload) accounts.each(&:reload)
expect(accounts.map(&:private_key)).to_not eq(old_private_keys) expect(accounts.map(&:private_key)).to_not eq(old_private_keys)
@ -993,7 +1000,8 @@ describe Mastodon::CLI::Accounts do
it 'broadcasts the new keys for each account' do it 'broadcasts the new keys for each account' do
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in) allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
subject expect { subject }
.to output_results('rotated')
accounts.each do |account| accounts.each do |account|
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
@ -1071,15 +1079,11 @@ describe Mastodon::CLI::Accounts do
allow(from_account).to receive(:destroy) allow(from_account).to receive(:destroy)
end end
it 'merges "from_account" into "to_account"' do it 'merges `from_account` into `to_account` and deletes `from_account`' do
subject expect { subject }
.to output_results('OK')
expect(to_account).to have_received(:merge_with!).with(from_account).once expect(to_account).to have_received(:merge_with!).with(from_account).once
end
it 'deletes "from_account"' do
subject
expect(from_account).to have_received(:destroy).once expect(from_account).to have_received(:destroy).once
end end
end end
@ -1099,15 +1103,11 @@ describe Mastodon::CLI::Accounts do
allow(from_account).to receive(:destroy) allow(from_account).to receive(:destroy)
end end
it 'merges "from_account" into "to_account"' do it 'merges "from_account" into "to_account" and deletes from_account' do
subject expect { subject }
.to output_results('OK')
expect(to_account).to have_received(:merge_with!).with(from_account).once expect(to_account).to have_received(:merge_with!).with(from_account).once
end
it 'deletes "from_account"' do
subject
expect(from_account).to have_received(:destroy) expect(from_account).to have_received(:destroy)
end end
end end
@ -1134,28 +1134,23 @@ describe Mastodon::CLI::Accounts do
stub_request(:head, 'https://example.net/users/tales').to_return(status: 200) stub_request(:head, 'https://example.net/users/tales').to_return(status: 200)
end end
it 'deletes all inactive remote accounts that longer exist in the origin server' do def expect_delete_inactive_remote_accounts
subject
expect(delete_account_service).to have_received(:call).with(bob, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(bob, reserve_username: false).once
expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once
end end
it 'does not delete any active remote account that still exists in the origin server' do def expect_not_delete_active_accounts
subject
expect(delete_account_service).to_not have_received(:call).with(tom, reserve_username: false) expect(delete_account_service).to_not have_received(:call).with(tom, reserve_username: false)
expect(delete_account_service).to_not have_received(:call).with(ana, reserve_username: false) expect(delete_account_service).to_not have_received(:call).with(ana, reserve_username: false)
expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false) expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false)
end end
it 'touches inactive remote accounts that have not been deleted' do it 'touches inactive remote accounts that have not been deleted and summarizes activity' do
expect { subject }.to(change { tales.reload.updated_at })
end
it 'displays the summary correctly' do
expect { subject } expect { subject }
.to output_results('Visited 5 accounts, removed 2') .to change { tales.reload.updated_at }
.and output_results('Visited 5 accounts, removed 2')
expect_delete_inactive_remote_accounts
expect_not_delete_active_accounts
end end
end end
@ -1168,16 +1163,15 @@ describe Mastodon::CLI::Accounts do
stub_request(:head, 'https://example.net/users/tales').to_return(status: 404) stub_request(:head, 'https://example.net/users/tales').to_return(status: 404)
end end
it 'deletes inactive remote accounts that longer exist in the specified domain' do def expect_delete_inactive_remote_accounts
subject
expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once
expect(delete_account_service).to have_received(:call).with(tales, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(tales, reserve_username: false).once
end end
it 'displays the summary correctly' do it 'displays the summary correctly and deletes inactive remote accounts' do
expect { subject } expect { subject }
.to output_results('Visited 2 accounts, removed 2') .to output_results('Visited 2 accounts, removed 2')
expect_delete_inactive_remote_accounts
end end
end end
@ -1189,15 +1183,14 @@ describe Mastodon::CLI::Accounts do
stub_request(:head, 'https://example.net/users/gon').to_return(status: 200) stub_request(:head, 'https://example.net/users/gon').to_return(status: 200)
end end
it 'skips accounts from the unavailable domain' do def expect_skip_accounts_from_unavailable_domain
subject
expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false) expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false)
end end
it 'displays the summary correctly' do it 'displays the summary correctly and skip accounts from unavailable domains' do
expect { subject } expect { subject }
.to output_results("Visited 5 accounts, removed 0\nThe following domains were not available during the check:\n example.net") .to output_results("Visited 5 accounts, removed 0\nThe following domains were not available during the check:\n example.net")
expect_skip_accounts_from_unavailable_domain
end end
end end
@ -1268,25 +1261,14 @@ describe Mastodon::CLI::Accounts do
before do before do
accounts.each { |account| target_account.follow!(account) } accounts.each { |account| target_account.follow!(account) }
end
it 'resets all "following" relationships from the target account' do
subject
expect(target_account.reload.following).to be_empty
end
it 'calls BootstrapTimelineWorker once to rebuild the timeline' do
allow(BootstrapTimelineWorker).to receive(:perform_async) allow(BootstrapTimelineWorker).to receive(:perform_async)
subject
expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once
end end
it 'displays a successful message' do it 'resets following relationships and displays a successful message and rebuilds timeline' do
expect { subject } expect { subject }
.to output_results("Processed #{total_relationships} relationships") .to output_results("Processed #{total_relationships} relationships")
expect(target_account.reload.following).to be_empty
expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once
end end
end end
@ -1297,15 +1279,10 @@ describe Mastodon::CLI::Accounts do
accounts.each { |account| account.follow!(target_account) } accounts.each { |account| account.follow!(target_account) }
end end
it 'resets all "followers" relationships from the target account' do it 'resets followers relationships and displays a successful message' do
subject
expect(target_account.reload.followers).to be_empty
end
it 'displays a successful message' do
expect { subject } expect { subject }
.to output_results("Processed #{total_relationships} relationships") .to output_results("Processed #{total_relationships} relationships")
expect(target_account.reload.followers).to be_empty
end end
end end
@ -1315,31 +1292,15 @@ describe Mastodon::CLI::Accounts do
before do before do
accounts.first(2).each { |account| account.follow!(target_account) } accounts.first(2).each { |account| account.follow!(target_account) }
accounts.last(1).each { |account| target_account.follow!(account) } accounts.last(1).each { |account| target_account.follow!(account) }
end
it 'resets all "followers" relationships from the target account' do
subject
expect(target_account.reload.followers).to be_empty
end
it 'resets all "following" relationships from the target account' do
subject
expect(target_account.reload.following).to be_empty
end
it 'calls BootstrapTimelineWorker once to rebuild the timeline' do
allow(BootstrapTimelineWorker).to receive(:perform_async) allow(BootstrapTimelineWorker).to receive(:perform_async)
subject
expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once
end end
it 'displays a successful message' do it 'resets followers and following and displays a successful message and rebuilds timeline' do
expect { subject } expect { subject }
.to output_results("Processed #{total_relationships} relationships") .to output_results("Processed #{total_relationships} relationships")
expect(target_account.reload.followers).to be_empty
expect(target_account.reload.following).to be_empty
expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once
end end
end end
end end
@ -1360,57 +1321,51 @@ describe Mastodon::CLI::Accounts do
stub_parallelize_with_progress! stub_parallelize_with_progress!
end end
it 'prunes all remote accounts with no interactions with local users' do def expect_prune_remote_accounts_without_interaction
subject
prunable_account_ids = prunable_accounts.pluck(:id) prunable_account_ids = prunable_accounts.pluck(:id)
expect(Account.where(id: prunable_account_ids).count).to eq(0) expect(Account.where(id: prunable_account_ids).count).to eq(0)
end end
it 'displays a successful message' do it 'displays a successful message and handles accounts correctly' do
expect { subject } expect { subject }
.to output_results("OK, pruned #{prunable_accounts.size} accounts") .to output_results("OK, pruned #{prunable_accounts.size} accounts")
expect_prune_remote_accounts_without_interaction
expect_not_prune_local_accounts
expect_not_prune_bot_accounts
expect_not_prune_group_accounts
expect_not_prune_mentioned_accounts
end end
it 'does not prune local accounts' do def expect_not_prune_local_accounts
subject
expect(Account.exists?(id: local_account.id)).to be(true) expect(Account.exists?(id: local_account.id)).to be(true)
end end
it 'does not prune bot accounts' do def expect_not_prune_bot_accounts
subject
expect(Account.exists?(id: bot_account.id)).to be(true) expect(Account.exists?(id: bot_account.id)).to be(true)
end end
it 'does not prune group accounts' do def expect_not_prune_group_accounts
subject
expect(Account.exists?(id: group_account.id)).to be(true) expect(Account.exists?(id: group_account.id)).to be(true)
end end
it 'does not prune accounts that have been mentioned' do def expect_not_prune_mentioned_accounts
subject
expect(Account.exists?(id: mentioned_account.id)).to be true expect(Account.exists?(id: mentioned_account.id)).to be true
end end
context 'with --dry-run option' do context 'with --dry-run option' do
let(:options) { { dry_run: true } } let(:options) { { dry_run: true } }
it 'does not prune any account' do def expect_no_account_prunes
subject
prunable_account_ids = prunable_accounts.pluck(:id) prunable_account_ids = prunable_accounts.pluck(:id)
expect(Account.where(id: prunable_account_ids).count).to eq(prunable_accounts.size) expect(Account.where(id: prunable_account_ids).count).to eq(prunable_accounts.size)
end end
it 'displays a successful message with (DRY RUN)' do it 'displays a successful message with (DRY RUN) and doesnt prune anything' do
expect { subject } expect { subject }
.to output_results("OK, pruned #{prunable_accounts.size} accounts (DRY RUN)") .to output_results("OK, pruned #{prunable_accounts.size} accounts (DRY RUN)")
expect_no_account_prunes
end end
end end
end end
@ -1430,7 +1385,8 @@ describe Mastodon::CLI::Accounts do
shared_examples 'a successful migration' do shared_examples 'a successful migration' do
it 'calls the MoveService for the last migration' do it 'calls the MoveService for the last migration' do
subject expect { subject }
.to output_results('OK')
last_migration = source_account.migrations.last last_migration = source_account.migrations.last
@ -1540,7 +1496,8 @@ describe Mastodon::CLI::Accounts do
end end
it 'creates a migration for the specified account with the target account' do it 'creates a migration for the specified account with the target account' do
subject expect { subject }
.to output_results('migrated')
last_migration = source_account.migrations.last last_migration = source_account.migrations.last

View file

@ -33,26 +33,25 @@ describe Mastodon::CLI::IpBlocks do
let(:arguments) { ip_list } let(:arguments) { ip_list }
shared_examples 'ip address blocking' do shared_examples 'ip address blocking' do
it 'blocks all specified IP addresses' do def blocked_ip_addresses
subject IpBlock.where(ip: ip_list).pluck(:ip)
blocked_ip_addresses = IpBlock.where(ip: ip_list).pluck(:ip)
expected_ip_addresses = ip_list.map { |ip| IPAddr.new(ip) }
expect(blocked_ip_addresses).to match_array(expected_ip_addresses)
end end
it 'sets the severity for all blocked IP addresses' do def expected_ip_addresses
subject ip_list.map { |ip| IPAddr.new(ip) }
blocked_ips_severity = IpBlock.where(ip: ip_list).pluck(:severity).all?(options[:severity])
expect(blocked_ips_severity).to be(true)
end end
it 'displays a success message with a summary' do def blocked_ips_severity
IpBlock.where(ip: ip_list).pluck(:severity).all?(options[:severity])
end
it 'blocks and sets severity for ip address and displays summary' do
expect { subject } expect { subject }
.to output_results("Added #{ip_list.size}, skipped 0, failed 0") .to output_results("Added #{ip_list.size}, skipped 0, failed 0")
expect(blocked_ip_addresses)
.to match_array(expected_ip_addresses)
expect(blocked_ips_severity)
.to be(true)
end end
end end
@ -64,17 +63,13 @@ describe Mastodon::CLI::IpBlocks do
let!(:blocked_ip) { IpBlock.create(ip: ip_list.last, severity: options[:severity]) } let!(:blocked_ip) { IpBlock.create(ip: ip_list.last, severity: options[:severity]) }
let(:arguments) { ip_list } let(:arguments) { ip_list }
it 'skips the already blocked IP address' do before { allow(IpBlock).to receive(:new).and_call_original }
allow(IpBlock).to receive(:new).and_call_original
subject it 'skips already block ip and displays the correct summary' do
expect(IpBlock).to_not have_received(:new).with(ip: ip_list.last)
end
it 'displays the correct summary' do
expect { subject } expect { subject }
.to output_results("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0") .to output_results("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0")
expect(IpBlock).to_not have_received(:new).with(ip: ip_list.last)
end end
context 'with --force option' do context 'with --force option' do
@ -83,7 +78,8 @@ describe Mastodon::CLI::IpBlocks do
it 'overwrites the existing IP block record' do it 'overwrites the existing IP block record' do
expect { subject } expect { subject }
.to change { blocked_ip.reload.severity } .to output_results('Added 11')
.and change { blocked_ip.reload.severity }
.from('no_access') .from('no_access')
.to('sign_up_requires_approval') .to('sign_up_requires_approval')
end end
@ -179,15 +175,10 @@ describe Mastodon::CLI::IpBlocks do
ip_list.each { |ip| IpBlock.create(ip: ip, severity: :no_access) } ip_list.each { |ip| IpBlock.create(ip: ip, severity: :no_access) }
end end
it 'removes exact IP blocks' do it 'removes exact ip blocks and displays success message with a summary' do
subject
expect(IpBlock.where(ip: ip_list)).to_not exist
end
it 'displays success message with a summary' do
expect { subject } expect { subject }
.to output_results("Removed #{ip_list.size}, skipped 0") .to output_results("Removed #{ip_list.size}, skipped 0")
expect(IpBlock.where(ip: ip_list)).to_not exist
end end
end end
@ -198,16 +189,20 @@ describe Mastodon::CLI::IpBlocks do
let(:arguments) { ['192.168.0.5', '10.0.1.50'] } let(:arguments) { ['192.168.0.5', '10.0.1.50'] }
let(:options) { { force: true } } let(:options) { { force: true } }
it 'removes blocks for IP ranges that cover given IP(s)' do it 'removes blocks for IP ranges that cover given IP(s) and keeps other ranges' do
subject expect { subject }
.to output_results('Removed 2')
expect(IpBlock.where(id: [first_ip_range_block.id, second_ip_range_block.id])).to_not exist expect(covered_ranges).to_not exist
expect(other_ranges).to exist
end end
it 'does not remove other IP ranges' do def covered_ranges
subject IpBlock.where(id: [first_ip_range_block.id, second_ip_range_block.id])
end
expect(IpBlock.where(id: third_ip_range_block.id)).to exist def other_ranges
IpBlock.where(id: third_ip_range_block.id)
end end
end end
@ -215,14 +210,12 @@ describe Mastodon::CLI::IpBlocks do
let(:unblocked_ip) { '192.0.2.1' } let(:unblocked_ip) { '192.0.2.1' }
let(:arguments) { [unblocked_ip] } let(:arguments) { [unblocked_ip] }
it 'skips the IP address' do it 'skips the IP address and displays summary' do
expect { subject } expect { subject }
.to output_results("#{unblocked_ip} is not yet blocked") .to output_results(
end "#{unblocked_ip} is not yet blocked",
'Removed 0, skipped 1'
it 'displays the summary correctly' do )
expect { subject }
.to output_results('Removed 0, skipped 1')
end end
end end
@ -230,14 +223,12 @@ describe Mastodon::CLI::IpBlocks do
let(:invalid_ip) { '320.15.175.0' } let(:invalid_ip) { '320.15.175.0' }
let(:arguments) { [invalid_ip] } let(:arguments) { [invalid_ip] }
it 'skips the invalid IP address' do it 'skips the invalid IP address and displays summary' do
expect { subject } expect { subject }
.to output_results("#{invalid_ip} is invalid") .to output_results(
end "#{invalid_ip} is invalid",
'Removed 0, skipped 1'
it 'displays the summary correctly' do )
expect { subject }
.to output_results('Removed 0, skipped 1')
end end
end end
@ -265,7 +256,7 @@ describe Mastodon::CLI::IpBlocks do
.to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") .to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}")
end end
it 'does not export bloked IPs with different severities' do it 'does not export blocked IPs with different severities' do
expect { subject } expect { subject }
.to_not output_results("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}") .to_not output_results("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}")
end end
@ -279,7 +270,7 @@ describe Mastodon::CLI::IpBlocks do
.to output_results("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};") .to output_results("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};")
end end
it 'does not export bloked IPs with different severities' do it 'does not export blocked IPs with different severities' do
expect { subject } expect { subject }
.to_not output_results("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};") .to_not output_results("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};")
end end

View file

@ -20,37 +20,30 @@ describe Mastodon::CLI::Settings do
describe '#open' do describe '#open' do
let(:action) { :open } let(:action) { :open }
it 'changes "registrations_mode" to "open"' do it 'changes "registrations_mode" to "open" and displays success' do
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('open')
end
it 'displays success message' do
expect { subject } expect { subject }
.to output_results('OK') .to change(Setting, :registrations_mode).from(nil).to('open')
.and output_results('OK')
end end
end end
describe '#approved' do describe '#approved' do
let(:action) { :approved } let(:action) { :approved }
it 'changes "registrations_mode" to "approved"' do it 'changes "registrations_mode" to "approved" and displays success' do
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved')
end
it 'displays success message' do
expect { subject } expect { subject }
.to output_results('OK') .to change(Setting, :registrations_mode).from(nil).to('approved')
.and output_results('OK')
end end
context 'with --require-reason' do context 'with --require-reason' do
let(:options) { { require_reason: true } } let(:options) { { require_reason: true } }
it 'changes "registrations_mode" to "approved"' do it 'changes registrations_mode and require_invite_text' do
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved') expect { subject }
end .to output_results('OK')
.and change(Setting, :registrations_mode).from(nil).to('approved')
it 'sets "require_invite_text" to "true"' do .and change(Setting, :require_invite_text).from(false).to(true)
expect { subject }.to change(Setting, :require_invite_text).from(false).to(true)
end end
end end
end end
@ -58,13 +51,10 @@ describe Mastodon::CLI::Settings do
describe '#close' do describe '#close' do
let(:action) { :close } let(:action) { :close }
it 'changes "registrations_mode" to "none"' do it 'changes "registrations_mode" to "none" and displays success' do
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('none')
end
it 'displays success message' do
expect { subject } expect { subject }
.to output_results('OK') .to change(Setting, :registrations_mode).from(nil).to('none')
.and output_results('OK')
end end
end end
end end

View file

@ -104,7 +104,6 @@ RSpec.configure do |config|
end end
config.before :each, type: :cli do config.before :each, type: :cli do
stub_stdout
stub_reset_connection_pools stub_reset_connection_pools
end end
@ -163,14 +162,6 @@ def attachment_fixture(name)
Rails.root.join('spec', 'fixtures', 'files', name).open Rails.root.join('spec', 'fixtures', 'files', name).open
end end
def stub_stdout
# TODO: Is there a bettery way to:
# - Avoid CLI command output being printed out
# - Allow rspec to assert things against STDOUT
# - Avoid disabling stdout for other desirable output (deprecation warnings, for example)
allow($stdout).to receive(:write)
end
def stub_reset_connection_pools def stub_reset_connection_pools
# TODO: Is there a better way to correctly run specs without stubbing this? # TODO: Is there a better way to correctly run specs without stubbing this?
# (Avoids reset_connection_pools! in test env) # (Avoids reset_connection_pools! in test env)

View file

@ -2,20 +2,16 @@
require 'rails_helper' require 'rails_helper'
describe Api::V1::Accounts::FamiliarFollowersController do describe 'Accounts Familiar Followers API' do
render_views
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:follows' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
before do describe 'GET /api/v1/accounts/familiar_followers' do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
it 'returns http success' do it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 } get '/api/v1/accounts/familiar_followers', params: { account_id: account.id, limit: 2 }, headers: headers
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
@ -26,7 +22,7 @@ describe Api::V1::Accounts::FamiliarFollowersController do
it 'removes duplicate account IDs from params' do it 'removes duplicate account IDs from params' do
account_ids = [account_a, account_b, account_b, account_a, account_a].map { |a| a.id.to_s } account_ids = [account_a, account_b, account_b, account_a, account_a].map { |a| a.id.to_s }
get :index, params: { id: account_ids } get '/api/v1/accounts/familiar_followers', params: { id: account_ids }, headers: headers
expect(body_as_json.pluck(:id)).to contain_exactly(account_a.id.to_s, account_b.id.to_s) expect(body_as_json.pluck(:id)).to contain_exactly(account_a.id.to_s, account_b.id.to_s)
end end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Accounts Identity Proofs API' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:accounts' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:account) { Fabricate(:account) }
describe 'GET /api/v1/accounts/identity_proofs' do
it 'returns http success' do
get "/api/v1/accounts/#{account.id}/identity_proofs", params: { limit: 2 }, headers: headers
expect(response).to have_http_status(200)
end
end
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Accounts Lists API' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:lists' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:account) { Fabricate(:account) }
let(:list) { Fabricate(:list, account: user.account) }
before do
user.account.follow!(account)
list.accounts << account
end
describe 'GET /api/v1/accounts/lists' do
it 'returns http success' do
get "/api/v1/accounts/#{account.id}/lists", headers: headers
expect(response).to have_http_status(200)
end
end
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Accounts Lookup API' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:accounts' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:account) { Fabricate(:account) }
describe 'GET /api/v1/accounts/lookup' do
it 'returns http success' do
get '/api/v1/accounts/lookup', params: { account_id: account.id, acct: account.acct }, headers: headers
expect(response).to have_http_status(200)
end
end
end

View file

@ -2,21 +2,17 @@
require 'rails_helper' require 'rails_helper'
describe Api::V1::Accounts::NotesController do describe 'Accounts Notes API' do
render_views
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:accounts') } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'write:accounts' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
let(:comment) { 'foo' } let(:comment) { 'foo' }
before do describe 'POST /api/v1/accounts/:account_id/note' do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'POST #create' do
subject do subject do
post :create, params: { account_id: account.id, comment: comment } post "/api/v1/accounts/#{account.id}/note", params: { comment: comment }, headers: headers
end end
context 'when account note has reasonable length', :aggregate_failures do context 'when account note has reasonable length', :aggregate_failures do

View file

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Accounts Pins API' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'write:accounts' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:kevin) { Fabricate(:user) }
before do
kevin.account.followers << user.account
end
describe 'POST /api/v1/accounts/:account_id/pin' do
subject { post "/api/v1/accounts/#{kevin.account.id}/pin", headers: headers }
it 'creates account_pin', :aggregate_failures do
expect do
subject
end.to change { AccountPin.where(account: user.account, target_account: kevin.account).count }.by(1)
expect(response).to have_http_status(200)
end
end
describe 'POST /api/v1/accounts/:account_id/unpin' do
subject { post "/api/v1/accounts/#{kevin.account.id}/unpin", headers: headers }
before do
Fabricate(:account_pin, account: user.account, target_account: kevin.account)
end
it 'destroys account_pin', :aggregate_failures do
expect do
subject
end.to change { AccountPin.where(account: user.account, target_account: kevin.account).count }.by(-1)
expect(response).to have_http_status(200)
end
end
end

View file

@ -31,8 +31,8 @@ describe 'GET /api/v1/accounts/relationships' do
.to have_http_status(200) .to have_http_status(200)
expect(body_as_json) expect(body_as_json)
.to be_an(Enumerable) .to be_an(Enumerable)
.and have_attributes( .and contain_exactly(
first: include( include(
following: true, following: true,
followed_by: false followed_by: false
) )
@ -53,9 +53,11 @@ describe 'GET /api/v1/accounts/relationships' do
expect(body_as_json) expect(body_as_json)
.to be_an(Enumerable) .to be_an(Enumerable)
.and have_attributes( .and have_attributes(
size: 2, size: 2
first: include(simon_item), )
second: include(lewis_item) .and contain_exactly(
include(simon_item),
include(lewis_item)
) )
end end
end end
@ -71,10 +73,12 @@ describe 'GET /api/v1/accounts/relationships' do
expect(body_as_json) expect(body_as_json)
.to be_an(Enumerable) .to be_an(Enumerable)
.and have_attributes( .and have_attributes(
size: 3, size: 3
first: include(simon_item), )
second: include(lewis_item), .and contain_exactly(
third: include(bob_item) include(simon_item),
include(lewis_item),
include(bob_item)
) )
end end
end end
@ -88,9 +92,11 @@ describe 'GET /api/v1/accounts/relationships' do
expect(body_as_json) expect(body_as_json)
.to be_an(Enumerable) .to be_an(Enumerable)
.and have_attributes( .and have_attributes(
size: 2, size: 2
first: include(simon_item), )
second: include(lewis_item) .and contain_exactly(
include(simon_item),
include(lewis_item)
) )
end end
end end
@ -116,7 +122,6 @@ describe 'GET /api/v1/accounts/relationships' do
muting: false, muting: false,
requested: false, requested: false,
domain_blocking: false, domain_blocking: false,
} }
end end
@ -129,7 +134,6 @@ describe 'GET /api/v1/accounts/relationships' do
muting: false, muting: false,
requested: false, requested: false,
domain_blocking: false, domain_blocking: false,
} }
end end
end end
@ -149,8 +153,10 @@ describe 'GET /api/v1/accounts/relationships' do
expect(body_as_json) expect(body_as_json)
.to be_an(Enumerable) .to be_an(Enumerable)
.and have_attributes( .and have_attributes(
size: 1, size: 1
first: include( )
.and contain_exactly(
include(
following: true, following: true,
showing_reblogs: true showing_reblogs: true
) )
@ -168,8 +174,8 @@ describe 'GET /api/v1/accounts/relationships' do
expect(body_as_json) expect(body_as_json)
.to be_an(Enumerable) .to be_an(Enumerable)
.and have_attributes( .and contain_exactly(
first: include( include(
following: false, following: false,
showing_reblogs: false showing_reblogs: false
) )

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Accounts Search API' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:accounts' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/accounts/search' do
it 'returns http success' do
get '/api/v1/accounts/search', params: { q: 'query' }, headers: headers
expect(response).to have_http_status(200)
end
end
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Featured Tags Suggestions API' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:accounts' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:account) { Fabricate(:account) }
describe 'GET /api/v1/featured_tags/suggestions' do
it 'returns http success' do
get '/api/v1/featured_tags/suggestions', params: { account_id: account.id, limit: 2 }, headers: headers
expect(response).to have_http_status(200)
end
end
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Suggestions API' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v2/suggestions' do
it 'returns http success' do
get '/api/v2/suggestions', headers: headers
expect(response).to have_http_status(200)
end
end
end

View file

@ -11,13 +11,14 @@ RSpec.describe FanOutOnWriteService, type: :service do
let(:visibility) { 'public' } let(:visibility) { 'public' }
let(:searchability) { 'public' } let(:searchability) { 'public' }
let(:subscription_policy) { :allow } let(:subscription_policy) { :allow }
let(:status) { Fabricate(:status, account: alice, visibility: visibility, searchability: searchability, text: 'Hello @bob #hoge') } let(:status) { Fabricate(:status, account: alice, visibility: visibility, searchability: searchability, text: 'Hello @bob @eve #hoge') }
let!(:alice) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { master_settings: { subscription_policy: subscription_policy } }).account } let!(:alice) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { master_settings: { subscription_policy: subscription_policy } }).account }
let!(:bob) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'bob' }).account } let!(:bob) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'bob' }).account }
let!(:tom) { Fabricate(:user, current_sign_in_at: last_active_at).account } let!(:tom) { Fabricate(:user, current_sign_in_at: last_active_at).account }
let!(:ohagi) { Fabricate(:user, current_sign_in_at: last_active_at).account } let!(:ohagi) { Fabricate(:user, current_sign_in_at: last_active_at).account }
let!(:tagf) { Fabricate(:user, current_sign_in_at: last_active_at).account } let!(:tagf) { Fabricate(:user, current_sign_in_at: last_active_at).account }
let!(:eve) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'eve' }).account }
let!(:list) { nil } let!(:list) { nil }
let!(:empty_list) { nil } let!(:empty_list) { nil }
@ -256,6 +257,25 @@ RSpec.describe FanOutOnWriteService, type: :service do
end end
end end
end end
context 'when handling status updates', :sidekiq_fake do
before do
subject.call(status)
status.snapshot!(at_time: status.created_at, rate_limit: false)
status.update!(text: 'Hello @bob @eve #hoge (edited)')
status.snapshot!(account_id: status.account_id)
redis.set("subscribed:timeline:#{eve.id}:notifications", '1')
Sidekiq::Worker.clear_all
end
it 'pushes the update to mentioned users through the notifications streaming channel' do
subject.call(status, update: true)
expect(PushUpdateWorker).to have_enqueued_sidekiq_job(anything, status.id, "timeline:#{eve.id}:notifications", { 'update' => true })
end
end
end end
context 'when status is limited' do context 'when status is limited' do

397
yarn.lock
View file

@ -52,7 +52,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5": "@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5":
version: 7.23.5 version: 7.23.5
resolution: "@babel/compat-data@npm:7.23.5" resolution: "@babel/compat-data@npm:7.23.5"
checksum: 081278ed46131a890ad566a59c61600a5f9557bd8ee5e535890c8548192532ea92590742fd74bd9db83d74c669ef8a04a7e1c85cdea27f960233e3b83c3a957c checksum: 081278ed46131a890ad566a59c61600a5f9557bd8ee5e535890c8548192532ea92590742fd74bd9db83d74c669ef8a04a7e1c85cdea27f960233e3b83c3a957c
@ -60,37 +60,37 @@ __metadata:
linkType: hard linkType: hard
"@babel/core@npm:^7.10.4, @babel/core@npm:^7.11.1, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.22.1": "@babel/core@npm:^7.10.4, @babel/core@npm:^7.11.1, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.22.1":
version: 7.23.5 version: 7.23.6
resolution: "@babel/core@npm:7.23.5" resolution: "@babel/core@npm:7.23.6"
dependencies: dependencies:
"@ampproject/remapping": "npm:^2.2.0" "@ampproject/remapping": "npm:^2.2.0"
"@babel/code-frame": "npm:^7.23.5" "@babel/code-frame": "npm:^7.23.5"
"@babel/generator": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6"
"@babel/helper-compilation-targets": "npm:^7.22.15" "@babel/helper-compilation-targets": "npm:^7.23.6"
"@babel/helper-module-transforms": "npm:^7.23.3" "@babel/helper-module-transforms": "npm:^7.23.3"
"@babel/helpers": "npm:^7.23.5" "@babel/helpers": "npm:^7.23.6"
"@babel/parser": "npm:^7.23.5" "@babel/parser": "npm:^7.23.6"
"@babel/template": "npm:^7.22.15" "@babel/template": "npm:^7.22.15"
"@babel/traverse": "npm:^7.23.5" "@babel/traverse": "npm:^7.23.6"
"@babel/types": "npm:^7.23.5" "@babel/types": "npm:^7.23.6"
convert-source-map: "npm:^2.0.0" convert-source-map: "npm:^2.0.0"
debug: "npm:^4.1.0" debug: "npm:^4.1.0"
gensync: "npm:^1.0.0-beta.2" gensync: "npm:^1.0.0-beta.2"
json5: "npm:^2.2.3" json5: "npm:^2.2.3"
semver: "npm:^6.3.1" semver: "npm:^6.3.1"
checksum: 311a512a870ee330a3f9a7ea89e5df790b2b5af0b1bd98b10b4edc0de2ac440f0df4d69ea2c0ee38a4b89041b9a495802741d93603be7d4fd834ec8bb6970bd2 checksum: a02bae7d916029b70706dc301535e1b31e5d216f55d4ee6f64a15825c6b69ee2c14c52a213d1497ec414e925ed4e9d897d41fb0d75df9fea28ed2c0008790e31
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/generator@npm:^7.23.5, @babel/generator@npm:^7.7.2": "@babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2":
version: 7.23.5 version: 7.23.6
resolution: "@babel/generator@npm:7.23.5" resolution: "@babel/generator@npm:7.23.6"
dependencies: dependencies:
"@babel/types": "npm:^7.23.5" "@babel/types": "npm:^7.23.6"
"@jridgewell/gen-mapping": "npm:^0.3.2" "@jridgewell/gen-mapping": "npm:^0.3.2"
"@jridgewell/trace-mapping": "npm:^0.3.17" "@jridgewell/trace-mapping": "npm:^0.3.17"
jsesc: "npm:^2.5.1" jsesc: "npm:^2.5.1"
checksum: 14c6e874f796c4368e919bed6003bb0adc3ce837760b08f9e646d20aeb5ae7d309723ce6e4f06bcb4a2b5753145446c8e4425851380f695e40e71e1760f49e7b checksum: 53540e905cd10db05d9aee0a5304e36927f455ce66f95d1253bb8a179f286b88fa7062ea0db354c566fe27f8bb96567566084ffd259f8feaae1de5eccc8afbda
languageName: node languageName: node
linkType: hard linkType: hard
@ -122,16 +122,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6": "@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.23.6":
version: 7.22.15 version: 7.23.6
resolution: "@babel/helper-compilation-targets@npm:7.22.15" resolution: "@babel/helper-compilation-targets@npm:7.23.6"
dependencies: dependencies:
"@babel/compat-data": "npm:^7.22.9" "@babel/compat-data": "npm:^7.23.5"
"@babel/helper-validator-option": "npm:^7.22.15" "@babel/helper-validator-option": "npm:^7.23.5"
browserslist: "npm:^4.21.9" browserslist: "npm:^4.22.2"
lru-cache: "npm:^5.1.1" lru-cache: "npm:^5.1.1"
semver: "npm:^6.3.1" semver: "npm:^6.3.1"
checksum: 45b9286861296e890f674a3abb199efea14a962a27d9b8adeb44970a9fd5c54e73a9e342e8414d2851cf4f98d5994537352fbce7b05ade32e9849bbd327f9ff1 checksum: ba38506d11185f48b79abf439462ece271d3eead1673dd8814519c8c903c708523428806f05f2ec5efd0c56e4e278698fac967e5a4b5ee842c32415da54bc6fa
languageName: node languageName: node
linkType: hard linkType: hard
@ -342,14 +342,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/helpers@npm:^7.23.5": "@babel/helpers@npm:^7.23.6":
version: 7.23.5 version: 7.23.6
resolution: "@babel/helpers@npm:7.23.5" resolution: "@babel/helpers@npm:7.23.6"
dependencies: dependencies:
"@babel/template": "npm:^7.22.15" "@babel/template": "npm:^7.22.15"
"@babel/traverse": "npm:^7.23.5" "@babel/traverse": "npm:^7.23.6"
"@babel/types": "npm:^7.23.5" "@babel/types": "npm:^7.23.6"
checksum: a37e2728eb4378a4888e5d614e28de7dd79b55ac8acbecd0e5c761273e2a02a8f33b34b1932d9069db55417ace2937cbf8ec37c42f1030ce6d228857d7ccaa4f checksum: df1cf6607676ad36f52f652ec03536f2732d70aef5e76dba5c964e34d49f3c2d3dcf9fb3740db359f53071d74b64606a833d5ba156f79f437f71bfe06e2e7e19
languageName: node languageName: node
linkType: hard linkType: hard
@ -364,12 +364,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.5": "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.6":
version: 7.23.5 version: 7.23.6
resolution: "@babel/parser@npm:7.23.5" resolution: "@babel/parser@npm:7.23.6"
bin: bin:
parser: ./bin/babel-parser.js parser: ./bin/babel-parser.js
checksum: 3356aa90d7bafb4e2c7310e7c2c3d443c4be4db74913f088d3d577a1eb914ea4188e05fd50a47ce907a27b755c4400c4e3cbeee73dbeb37761f6ca85954f5a20 checksum: 6f76cd5ccae1fa9bcab3525b0865c6222e9c1d22f87abc69f28c5c7b2c8816a13361f5bd06bddbd5faf903f7320a8feba02545c981468acec45d12a03db7755e
languageName: node languageName: node
linkType: hard linkType: hard
@ -836,14 +836,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/plugin-transform-for-of@npm:^7.23.3": "@babel/plugin-transform-for-of@npm:^7.23.6":
version: 7.23.3 version: 7.23.6
resolution: "@babel/plugin-transform-for-of@npm:7.23.3" resolution: "@babel/plugin-transform-for-of@npm:7.23.6"
dependencies: dependencies:
"@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/helper-plugin-utils": "npm:^7.22.5"
"@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5"
peerDependencies: peerDependencies:
"@babel/core": ^7.0.0-0 "@babel/core": ^7.0.0-0
checksum: 8a36202cfee312ba80e509c7c2131e6773524e572b4dc64a8ee95bd912634fdeb5ea91c6c7747ee30e03562d0f0d333f88ed7dbb929b36b60b8d74189189e12f checksum: 46681b6ab10f3ca2d961f50d4096b62ab5d551e1adad84e64be1ee23e72eb2f26a1e30e617e853c74f1349fffe4af68d33921a128543b6f24b6d46c09a3e2aec
languageName: node languageName: node
linkType: hard linkType: hard
@ -1200,8 +1201,8 @@ __metadata:
linkType: hard linkType: hard
"@babel/plugin-transform-runtime@npm:^7.22.4": "@babel/plugin-transform-runtime@npm:^7.22.4":
version: 7.23.4 version: 7.23.6
resolution: "@babel/plugin-transform-runtime@npm:7.23.4" resolution: "@babel/plugin-transform-runtime@npm:7.23.6"
dependencies: dependencies:
"@babel/helper-module-imports": "npm:^7.22.15" "@babel/helper-module-imports": "npm:^7.22.15"
"@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/helper-plugin-utils": "npm:^7.22.5"
@ -1211,7 +1212,7 @@ __metadata:
semver: "npm:^6.3.1" semver: "npm:^6.3.1"
peerDependencies: peerDependencies:
"@babel/core": ^7.0.0-0 "@babel/core": ^7.0.0-0
checksum: 6ac29012550cdd10b65ec43fef0c7f43904ec458c43d597f627d8f52807413e57ea94e3986dbace576d734e67c2d09be5e43e77c72567d18f8c4ac5e19844625 checksum: 94a7ee92f073df53fd8bebf9ed391a95553716077da1c6c3a57f10f042358c938495d55e6b09b4b50544c01f03560c4770c17698e1c24817a15d3668e8231249
languageName: node languageName: node
linkType: hard linkType: hard
@ -1333,11 +1334,11 @@ __metadata:
linkType: hard linkType: hard
"@babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.12.1, @babel/preset-env@npm:^7.22.4": "@babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.12.1, @babel/preset-env@npm:^7.22.4":
version: 7.23.5 version: 7.23.6
resolution: "@babel/preset-env@npm:7.23.5" resolution: "@babel/preset-env@npm:7.23.6"
dependencies: dependencies:
"@babel/compat-data": "npm:^7.23.5" "@babel/compat-data": "npm:^7.23.5"
"@babel/helper-compilation-targets": "npm:^7.22.15" "@babel/helper-compilation-targets": "npm:^7.23.6"
"@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/helper-plugin-utils": "npm:^7.22.5"
"@babel/helper-validator-option": "npm:^7.23.5" "@babel/helper-validator-option": "npm:^7.23.5"
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.23.3" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.23.3"
@ -1377,7 +1378,7 @@ __metadata:
"@babel/plugin-transform-dynamic-import": "npm:^7.23.4" "@babel/plugin-transform-dynamic-import": "npm:^7.23.4"
"@babel/plugin-transform-exponentiation-operator": "npm:^7.23.3" "@babel/plugin-transform-exponentiation-operator": "npm:^7.23.3"
"@babel/plugin-transform-export-namespace-from": "npm:^7.23.4" "@babel/plugin-transform-export-namespace-from": "npm:^7.23.4"
"@babel/plugin-transform-for-of": "npm:^7.23.3" "@babel/plugin-transform-for-of": "npm:^7.23.6"
"@babel/plugin-transform-function-name": "npm:^7.23.3" "@babel/plugin-transform-function-name": "npm:^7.23.3"
"@babel/plugin-transform-json-strings": "npm:^7.23.4" "@babel/plugin-transform-json-strings": "npm:^7.23.4"
"@babel/plugin-transform-literals": "npm:^7.23.3" "@babel/plugin-transform-literals": "npm:^7.23.3"
@ -1418,7 +1419,7 @@ __metadata:
semver: "npm:^6.3.1" semver: "npm:^6.3.1"
peerDependencies: peerDependencies:
"@babel/core": ^7.0.0-0 "@babel/core": ^7.0.0-0
checksum: 2a0e1274dec045186e131c6433659b75492583290e8d41633c616f6bff829cb2e4b2f9a57f556283a54db3bd6aa697911e56a36f607911a29b731c445a5b5a06 checksum: 5b24d179af52f082d04b9b98cc4777e37bf31a97cef5a91d8917e996dbd75f2f743c88c40f80744cb8529355bb674619d150c0260c32d834aa4067e21d0c8962
languageName: node languageName: node
linkType: hard linkType: hard
@ -1483,11 +1484,11 @@ __metadata:
linkType: hard linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": "@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.23.5 version: 7.23.6
resolution: "@babel/runtime@npm:7.23.5" resolution: "@babel/runtime@npm:7.23.6"
dependencies: dependencies:
regenerator-runtime: "npm:^0.14.0" regenerator-runtime: "npm:^0.14.0"
checksum: ca679cc91bb7e424bc2db87bb58cc3b06ade916b9adb21fbbdc43e54cdaacb3eea201ceba2a0464b11d2eb65b9fe6a6ffcf4d7521fa52994f19be96f1af14788 checksum: d886954e985ef8e421222f7a2848884d96a752e0020d3078b920dd104e672fdf23bcc6f51a44313a048796319f1ac9d09c2c88ec8cbb4e1f09174bcd3335b9ff
languageName: node languageName: node
linkType: hard linkType: hard
@ -1502,32 +1503,32 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/traverse@npm:7, @babel/traverse@npm:^7.23.5": "@babel/traverse@npm:7, @babel/traverse@npm:^7.23.6":
version: 7.23.5 version: 7.23.6
resolution: "@babel/traverse@npm:7.23.5" resolution: "@babel/traverse@npm:7.23.6"
dependencies: dependencies:
"@babel/code-frame": "npm:^7.23.5" "@babel/code-frame": "npm:^7.23.5"
"@babel/generator": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6"
"@babel/helper-environment-visitor": "npm:^7.22.20" "@babel/helper-environment-visitor": "npm:^7.22.20"
"@babel/helper-function-name": "npm:^7.23.0" "@babel/helper-function-name": "npm:^7.23.0"
"@babel/helper-hoist-variables": "npm:^7.22.5" "@babel/helper-hoist-variables": "npm:^7.22.5"
"@babel/helper-split-export-declaration": "npm:^7.22.6" "@babel/helper-split-export-declaration": "npm:^7.22.6"
"@babel/parser": "npm:^7.23.5" "@babel/parser": "npm:^7.23.6"
"@babel/types": "npm:^7.23.5" "@babel/types": "npm:^7.23.6"
debug: "npm:^4.1.0" debug: "npm:^4.3.1"
globals: "npm:^11.1.0" globals: "npm:^11.1.0"
checksum: c5ea793080ca6719b0a1612198fd25e361cee1f3c14142d7a518d2a1eeb5c1d21f7eec1b26c20ea6e1ddd8ed12ab50b960ff95ffd25be353b6b46e1b54d6f825 checksum: 5b4ebb94a00a7e1daf111e4b0b45a7998d5b7598637a14e75e855e88cc1b702789e09a958726b5d599a003be1e9032dbdfde4b88ea6061332228738950d5582d
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": "@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
version: 7.23.5 version: 7.23.6
resolution: "@babel/types@npm:7.23.5" resolution: "@babel/types@npm:7.23.6"
dependencies: dependencies:
"@babel/helper-string-parser": "npm:^7.23.4" "@babel/helper-string-parser": "npm:^7.23.4"
"@babel/helper-validator-identifier": "npm:^7.22.20" "@babel/helper-validator-identifier": "npm:^7.22.20"
to-fast-properties: "npm:^2.0.0" to-fast-properties: "npm:^2.0.0"
checksum: 7dd5e2f59828ed046ad0b06b039df2524a8b728d204affb4fc08da2502b9dd3140b1356b5166515d229dc811539a8b70dcd4bc507e06d62a89f4091a38d0b0fb checksum: 42cefce8a68bd09bb5828b4764aa5586c53c60128ac2ac012e23858e1c179347a4aac9c66fc577994fbf57595227611c5ec8270bf0cfc94ff033bbfac0550b70
languageName: node languageName: node
linkType: hard linkType: hard
@ -1726,9 +1727,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/eslintrc@npm:^2.1.3": "@eslint/eslintrc@npm:^2.1.4":
version: 2.1.3 version: 2.1.4
resolution: "@eslint/eslintrc@npm:2.1.3" resolution: "@eslint/eslintrc@npm:2.1.4"
dependencies: dependencies:
ajv: "npm:^6.12.4" ajv: "npm:^6.12.4"
debug: "npm:^4.3.2" debug: "npm:^4.3.2"
@ -1739,14 +1740,14 @@ __metadata:
js-yaml: "npm:^4.1.0" js-yaml: "npm:^4.1.0"
minimatch: "npm:^3.1.2" minimatch: "npm:^3.1.2"
strip-json-comments: "npm:^3.1.1" strip-json-comments: "npm:^3.1.1"
checksum: f4103f4346126292eb15581c5a1d12bef03410fd3719dedbdb92e1f7031d46a5a2d60de8566790445d5d4b70b75ba050876799a11f5fff8265a91ee3fa77dab0 checksum: 32f67052b81768ae876c84569ffd562491ec5a5091b0c1e1ca1e0f3c24fb42f804952fdd0a137873bc64303ba368a71ba079a6f691cee25beee9722d94cc8573
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/js@npm:8.54.0": "@eslint/js@npm:8.55.0":
version: 8.54.0 version: 8.55.0
resolution: "@eslint/js@npm:8.54.0" resolution: "@eslint/js@npm:8.55.0"
checksum: d61fb4a0be6af2d8cb290121c329697664a75d6255a29926d5454fb02aeb02b87112f67fdf218d10abac42f90c570ac366126751baefc5405d0e017ed0c946c5 checksum: 88ab9fc57a651becd2b32ec40a3958db27fae133b1ae77bebd733aa5bbd00a92f325bb02f20ad680d31c731fa49b22f060a4777dd52eb3e27da013d940bd978d
languageName: node languageName: node
linkType: hard linkType: hard
@ -2451,7 +2452,7 @@ __metadata:
stacktrace-js: "npm:^2.0.2" stacktrace-js: "npm:^2.0.2"
stringz: "npm:^2.1.0" stringz: "npm:^2.1.0"
stylelint: "npm:^15.10.1" stylelint: "npm:^15.10.1"
stylelint-config-standard-scss: "npm:^11.0.0" stylelint-config-standard-scss: "npm:^12.0.0"
substring-trie: "npm:^1.0.2" substring-trie: "npm:^1.0.2"
terser-webpack-plugin: "npm:^4.2.3" terser-webpack-plugin: "npm:^4.2.3"
tesseract.js: "npm:^2.1.5" tesseract.js: "npm:^2.1.5"
@ -3046,11 +3047,11 @@ __metadata:
linkType: hard linkType: hard
"@types/emoji-mart@npm:^3.0.9": "@types/emoji-mart@npm:^3.0.9":
version: 3.0.13 version: 3.0.14
resolution: "@types/emoji-mart@npm:3.0.13" resolution: "@types/emoji-mart@npm:3.0.14"
dependencies: dependencies:
"@types/react": "npm:*" "@types/react": "npm:*"
checksum: 840f920c3242e1d274f0102e67cb2d00434e1fd370e0bcc8983b43b6b62322b01e3ddcd5fb078c60883e613530a7c70b8c40060624897543cd4da9441ca81486 checksum: 23ded65fce9b3355fbe903d3971cb67cc827a5d587464bb7e3f349615527ef4a9197b3bb59fa84c4391d1b901e7f200f686a7fc83f649ae2a51a0fb948cbadfb
languageName: node languageName: node
linkType: hard linkType: hard
@ -3194,12 +3195,12 @@ __metadata:
linkType: hard linkType: hard
"@types/jest@npm:^29.5.2": "@types/jest@npm:^29.5.2":
version: 29.5.10 version: 29.5.11
resolution: "@types/jest@npm:29.5.10" resolution: "@types/jest@npm:29.5.11"
dependencies: dependencies:
expect: "npm:^29.0.0" expect: "npm:^29.0.0"
pretty-format: "npm:^29.0.0" pretty-format: "npm:^29.0.0"
checksum: b46171d59d12a5f69bbe710f65eaf59a8073337c6b4a67dff8158575caec53f1c61f8a7d645b34d6ac3c4ea398acd30f0c5d1c4a131c0c918798019264a3397d checksum: 524a3394845214581278bf4d75055927261fbeac7e1a89cd621bd0636da37d265fe0a85eac58b5778758faad1cbd7c7c361dfc190c78ebde03a91cce33463261
languageName: node languageName: node
linkType: hard linkType: hard
@ -3393,11 +3394,11 @@ __metadata:
linkType: hard linkType: hard
"@types/react-helmet@npm:^6.1.6": "@types/react-helmet@npm:^6.1.6":
version: 6.1.9 version: 6.1.11
resolution: "@types/react-helmet@npm:6.1.9" resolution: "@types/react-helmet@npm:6.1.11"
dependencies: dependencies:
"@types/react": "npm:*" "@types/react": "npm:*"
checksum: d1823582903d6e70f1f447c7bec9e844b6f85f5de84cbcde5c8bbeecc064db1394c786ed9b9ded30544afe5c91e57c7e8105171df1643998f64c0aeab9f7f2aa checksum: f7b3bb2151d992a108ae46fed876fb9c8119108397d9a01d150c5642782997542c8b3c52e742b56e8689b7dbfa62ca9cfc76aa7e05dec4e60c652f7ef53fa783
languageName: node languageName: node
linkType: hard linkType: hard
@ -3514,13 +3515,13 @@ __metadata:
linkType: hard linkType: hard
"@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7": "@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7":
version: 18.2.41 version: 18.2.43
resolution: "@types/react@npm:18.2.41" resolution: "@types/react@npm:18.2.43"
dependencies: dependencies:
"@types/prop-types": "npm:*" "@types/prop-types": "npm:*"
"@types/scheduler": "npm:*" "@types/scheduler": "npm:*"
csstype: "npm:^3.0.2" csstype: "npm:^3.0.2"
checksum: 5cc72491ce8be95e7bbedd8bf039ca971772ecd22d989feb045af7e73247c7e6cff25a2f1c2200be461fb2f6b5aacef739e1ba9fd83c744209dfd3ce8aa75afe checksum: 10477a50fbd3c0cc5b8a2ade679f442717f68fb27c8460b2aa1d3256cd18c48f742bbe5b9ee37a8c4c5f832ffa37b3a23c09fd96dd880a8e3182d8929c05e803
languageName: node languageName: node
linkType: hard linkType: hard
@ -3701,14 +3702,14 @@ __metadata:
linkType: hard linkType: hard
"@typescript-eslint/eslint-plugin@npm:^6.0.0": "@typescript-eslint/eslint-plugin@npm:^6.0.0":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/eslint-plugin@npm:6.11.0" resolution: "@typescript-eslint/eslint-plugin@npm:6.13.2"
dependencies: dependencies:
"@eslint-community/regexpp": "npm:^4.5.1" "@eslint-community/regexpp": "npm:^4.5.1"
"@typescript-eslint/scope-manager": "npm:6.11.0" "@typescript-eslint/scope-manager": "npm:6.13.2"
"@typescript-eslint/type-utils": "npm:6.11.0" "@typescript-eslint/type-utils": "npm:6.13.2"
"@typescript-eslint/utils": "npm:6.11.0" "@typescript-eslint/utils": "npm:6.13.2"
"@typescript-eslint/visitor-keys": "npm:6.11.0" "@typescript-eslint/visitor-keys": "npm:6.13.2"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
graphemer: "npm:^1.4.0" graphemer: "npm:^1.4.0"
ignore: "npm:^5.2.4" ignore: "npm:^5.2.4"
@ -3721,44 +3722,44 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 6645aa09b9d51c5e3ea781eaf74da75b94f83f3e2d7b3dd988d5ce7eb82dd87e3509471cf2ee8c6b2428d907df5f1b02f29dbd04f54c2653f9566c8c4ce98009 checksum: 531a4406d872738d165c6a66cb26e976523c94053b022a8210dc9fd10e91b79b705bc0fcc77145e9744e4108b53bdba55e02a10dc17757b22be92aff57849384
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/parser@npm:^6.0.0": "@typescript-eslint/parser@npm:^6.0.0":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/parser@npm:6.11.0" resolution: "@typescript-eslint/parser@npm:6.13.2"
dependencies: dependencies:
"@typescript-eslint/scope-manager": "npm:6.11.0" "@typescript-eslint/scope-manager": "npm:6.13.2"
"@typescript-eslint/types": "npm:6.11.0" "@typescript-eslint/types": "npm:6.13.2"
"@typescript-eslint/typescript-estree": "npm:6.11.0" "@typescript-eslint/typescript-estree": "npm:6.13.2"
"@typescript-eslint/visitor-keys": "npm:6.11.0" "@typescript-eslint/visitor-keys": "npm:6.13.2"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
peerDependencies: peerDependencies:
eslint: ^7.0.0 || ^8.0.0 eslint: ^7.0.0 || ^8.0.0
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: e7caeb20069102e21f468fc0dbe7ff6fb6b1efa9e72f4c9f39d4a865ed0633f39130b593ef9ae8f394ca1d70563e15410faf30a482a97809951eaac6ed3a67da checksum: 2c62b8cd8a37eb2ea59cd00e559f51a9f57af746e2040e872af3c58ddd3f4071ad7b7009789bdeb0e0d4ee0343bfe96ee77288020f3ae22d08e1674203f5e156
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/scope-manager@npm:6.11.0": "@typescript-eslint/scope-manager@npm:6.13.2":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/scope-manager@npm:6.11.0" resolution: "@typescript-eslint/scope-manager@npm:6.13.2"
dependencies: dependencies:
"@typescript-eslint/types": "npm:6.11.0" "@typescript-eslint/types": "npm:6.13.2"
"@typescript-eslint/visitor-keys": "npm:6.11.0" "@typescript-eslint/visitor-keys": "npm:6.13.2"
checksum: d8999e2d1a4cbde8a79df5e3ec416f0e3db9532d39f2f4bb5a0ebdf954ae75c183d3277579ba05268fe2c88e88ef87f0fa12f02bb8d95d9e67d92e411241f3a3 checksum: 9b159e5bb10dfb5953e71488200b4126378fc7e987ce7d90946aea9ec40cd66c7ada92399657c5d9794189b764ca6f4eb38a8dcb9e4c5aa50ab6000a39636b9c
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/type-utils@npm:6.11.0": "@typescript-eslint/type-utils@npm:6.13.2":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/type-utils@npm:6.11.0" resolution: "@typescript-eslint/type-utils@npm:6.13.2"
dependencies: dependencies:
"@typescript-eslint/typescript-estree": "npm:6.11.0" "@typescript-eslint/typescript-estree": "npm:6.13.2"
"@typescript-eslint/utils": "npm:6.11.0" "@typescript-eslint/utils": "npm:6.13.2"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
ts-api-utils: "npm:^1.0.1" ts-api-utils: "npm:^1.0.1"
peerDependencies: peerDependencies:
@ -3766,23 +3767,23 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: ff68f2e052b8d688f1dc1a0050746704c8e0ab6263b47f1f52da73a7d251678e4950af23a95e1cd8e3fcea2457e6e5294ddbe01d29dafa2fdfb5b11ed9452a3f checksum: 1ca97c78abdf479aea0c54e869fda2ae2f69de1974cc063062ce7b5b16c7fdf497ea15c50a29dd5941ea1b6b77e8f1213a5c272a747e334ac69ede083f327468
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/types@npm:6.11.0": "@typescript-eslint/types@npm:6.13.2":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/types@npm:6.11.0" resolution: "@typescript-eslint/types@npm:6.13.2"
checksum: 23182813db39a5e9b9bcc1e85306c953f7b8b22d3885e41fcac0bd725c170fbcb70f4ce55633678cc5921dcf062fa0e55635eb39480c118a4411a00354820223 checksum: 029918ca5b1442bb4bc435773504ce32191e2c3e2fde8d4176bb6513f03e3dfa2aa9724b2d22b1640656d666b97f7a7ebfeaf67b881d5e07250828fa83e3ebe8
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/typescript-estree@npm:6.11.0": "@typescript-eslint/typescript-estree@npm:6.13.2":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/typescript-estree@npm:6.11.0" resolution: "@typescript-eslint/typescript-estree@npm:6.13.2"
dependencies: dependencies:
"@typescript-eslint/types": "npm:6.11.0" "@typescript-eslint/types": "npm:6.13.2"
"@typescript-eslint/visitor-keys": "npm:6.11.0" "@typescript-eslint/visitor-keys": "npm:6.13.2"
debug: "npm:^4.3.4" debug: "npm:^4.3.4"
globby: "npm:^11.1.0" globby: "npm:^11.1.0"
is-glob: "npm:^4.0.3" is-glob: "npm:^4.0.3"
@ -3791,34 +3792,34 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
checksum: 3e183e554e1bc74f065da3015f7137eb40c262f989c547701b1e3f4f20134e574e56b749288cd00d77b9d1ddb705546613c2457661ffc63b6060ffa97ba3aac8 checksum: 1c4c59dce0c51fdfee34d9f418e64fe28e3ec1a97661efc8a3d2780bdff36aff38de9090d356a968f394fa6d4e9c058936ce9cd260d4c44a52761ecd74915bce
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/utils@npm:6.11.0, @typescript-eslint/utils@npm:^6.5.0": "@typescript-eslint/utils@npm:6.13.2, @typescript-eslint/utils@npm:^6.5.0":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/utils@npm:6.11.0" resolution: "@typescript-eslint/utils@npm:6.13.2"
dependencies: dependencies:
"@eslint-community/eslint-utils": "npm:^4.4.0" "@eslint-community/eslint-utils": "npm:^4.4.0"
"@types/json-schema": "npm:^7.0.12" "@types/json-schema": "npm:^7.0.12"
"@types/semver": "npm:^7.5.0" "@types/semver": "npm:^7.5.0"
"@typescript-eslint/scope-manager": "npm:6.11.0" "@typescript-eslint/scope-manager": "npm:6.13.2"
"@typescript-eslint/types": "npm:6.11.0" "@typescript-eslint/types": "npm:6.13.2"
"@typescript-eslint/typescript-estree": "npm:6.11.0" "@typescript-eslint/typescript-estree": "npm:6.13.2"
semver: "npm:^7.5.4" semver: "npm:^7.5.4"
peerDependencies: peerDependencies:
eslint: ^7.0.0 || ^8.0.0 eslint: ^7.0.0 || ^8.0.0
checksum: c91eb4578607959acc2b43ddc791571682e45601a19b25d5d120786ed4af607656f83c5c1fa71972e549ddfb5542acf2f7d443ae93b32ee28192c22c106b8883 checksum: 84969be91e7949868eaaa289288c9d71927f0e427b572501b0991d8d62b40a4234f7287c35b35d276ccbb53e9ea5457b8250fcf4941e60e6b9ba4065fbfba416
languageName: node languageName: node
linkType: hard linkType: hard
"@typescript-eslint/visitor-keys@npm:6.11.0": "@typescript-eslint/visitor-keys@npm:6.13.2":
version: 6.11.0 version: 6.13.2
resolution: "@typescript-eslint/visitor-keys@npm:6.11.0" resolution: "@typescript-eslint/visitor-keys@npm:6.13.2"
dependencies: dependencies:
"@typescript-eslint/types": "npm:6.11.0" "@typescript-eslint/types": "npm:6.13.2"
eslint-visitor-keys: "npm:^3.4.1" eslint-visitor-keys: "npm:^3.4.1"
checksum: 5f48329422b7f286196661d39e93e9defd7c5cf80e6c84c8d03459853f5d9f86a5e91c5e80ea572dcdb907ebbe503bbcc77aeb8b468c294b2aa7b3ccfc81cb88 checksum: c173bc1fcc42c3075a5ee094e7f3bf0279d98315c25ff49e20d02d79022b1d0402accfa113b070afb4d52a6f6d180594b67baa8b6a784eabdf82b54dd1ff454c
languageName: node languageName: node
linkType: hard linkType: hard
@ -5189,17 +5190,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.21.9, browserslist@npm:^4.22.1": "browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.22.1, browserslist@npm:^4.22.2":
version: 4.22.1 version: 4.22.2
resolution: "browserslist@npm:4.22.1" resolution: "browserslist@npm:4.22.2"
dependencies: dependencies:
caniuse-lite: "npm:^1.0.30001541" caniuse-lite: "npm:^1.0.30001565"
electron-to-chromium: "npm:^1.4.535" electron-to-chromium: "npm:^1.4.601"
node-releases: "npm:^2.0.13" node-releases: "npm:^2.0.14"
update-browserslist-db: "npm:^1.0.13" update-browserslist-db: "npm:^1.0.13"
bin: bin:
browserslist: cli.js browserslist: cli.js
checksum: 6810f2d63f171d0b7b8d38cf091708e00cb31525501810a507839607839320d66e657293b0aa3d7f051ecbc025cb07390a90c037682c1d05d12604991e41050b checksum: 2a331aab90503130043ca41dd5d281fa1e89d5e076d07a2d75e76bf4d693bd56e73d5abcd8c4f39119da6328d450578c216cf1cd5c99b82d8a90a2ae6271b465
languageName: node languageName: node
linkType: hard linkType: hard
@ -5427,10 +5428,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001541": "caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001565":
version: 1.0.30001561 version: 1.0.30001568
resolution: "caniuse-lite@npm:1.0.30001561" resolution: "caniuse-lite@npm:1.0.30001568"
checksum: 6e84c84026fee53edbdbb5aded7a04a036aae4c2e367cf6bdc90c6783a591e2fdcfcdebcc4e774aca61092e542a61200c8c16b06659396492426033c4dbcc618 checksum: 13f01e5a2481134bd61cf565ce9fecbd8e107902927a0dcf534230a92191a81f1715792170f5f39719c767c3a96aa6df9917a8d5601f15bbd5e4041a8cfecc99
languageName: node languageName: node
linkType: hard linkType: hard
@ -6441,7 +6442,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.4": "debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4":
version: 4.3.4 version: 4.3.4
resolution: "debug@npm:4.3.4" resolution: "debug@npm:4.3.4"
dependencies: dependencies:
@ -6983,10 +6984,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"electron-to-chromium@npm:^1.4.535": "electron-to-chromium@npm:^1.4.601":
version: 1.4.576 version: 1.4.609
resolution: "electron-to-chromium@npm:1.4.576" resolution: "electron-to-chromium@npm:1.4.609"
checksum: b0b9e7ba803bf93ffac9cb830ed2b0e0eb07f20066127065f9ab9e08e4e6a5812040e03d76f6ee9bc59e03fb938fd414e83d4883b29111303e9e88633cf2dce4 checksum: 9675a79388acbaff5953a4c61589af7da93e0d1f9d6a3b284c7630f10126eb0998557b07448514214d5a3d19025310039b55f405ab701b1253130fc94907f743
languageName: node languageName: node
linkType: hard linkType: hard
@ -7341,13 +7342,13 @@ __metadata:
linkType: hard linkType: hard
"eslint-config-prettier@npm:^9.0.0": "eslint-config-prettier@npm:^9.0.0":
version: 9.0.0 version: 9.1.0
resolution: "eslint-config-prettier@npm:9.0.0" resolution: "eslint-config-prettier@npm:9.1.0"
peerDependencies: peerDependencies:
eslint: ">=7.0.0" eslint: ">=7.0.0"
bin: bin:
eslint-config-prettier: bin/cli.js eslint-config-prettier: bin/cli.js
checksum: bc1f661915845c631824178942e5d02f858fe6d0ea796f0050d63e0f681927b92696e81139dd04714c08c3e7de580fd079c66162e40070155ba79eaee78ab5d0 checksum: 6d332694b36bc9ac6fdb18d3ca2f6ac42afa2ad61f0493e89226950a7091e38981b66bac2b47ba39d15b73fff2cd32c78b850a9cf9eed9ca9a96bfb2f3a2f10d
languageName: node languageName: node
linkType: hard linkType: hard
@ -7583,13 +7584,13 @@ __metadata:
linkType: hard linkType: hard
"eslint@npm:^8.41.0": "eslint@npm:^8.41.0":
version: 8.54.0 version: 8.55.0
resolution: "eslint@npm:8.54.0" resolution: "eslint@npm:8.55.0"
dependencies: dependencies:
"@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/eslint-utils": "npm:^4.2.0"
"@eslint-community/regexpp": "npm:^4.6.1" "@eslint-community/regexpp": "npm:^4.6.1"
"@eslint/eslintrc": "npm:^2.1.3" "@eslint/eslintrc": "npm:^2.1.4"
"@eslint/js": "npm:8.54.0" "@eslint/js": "npm:8.55.0"
"@humanwhocodes/config-array": "npm:^0.11.13" "@humanwhocodes/config-array": "npm:^0.11.13"
"@humanwhocodes/module-importer": "npm:^1.0.1" "@humanwhocodes/module-importer": "npm:^1.0.1"
"@nodelib/fs.walk": "npm:^1.2.8" "@nodelib/fs.walk": "npm:^1.2.8"
@ -7626,7 +7627,7 @@ __metadata:
text-table: "npm:^0.2.0" text-table: "npm:^0.2.0"
bin: bin:
eslint: bin/eslint.js eslint: bin/eslint.js
checksum: 4f205f832bdbd0218cde374b067791f4f76d7abe8de86b2dc849c273899051126d912ebf71531ee49b8eeaa22cad77febdc8f2876698dc2a76e84a8cb976af22 checksum: d28c0b60f19bb7d355cb8393e77b018c8f548dba3f820b799c89bb2e0c436ee26084e700c5e57e1e97e7972ec93065277849141b82e7b0c0d02c2dc1e553a2a1
languageName: node languageName: node
linkType: hard linkType: hard
@ -11958,10 +11959,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"node-releases@npm:^2.0.13": "node-releases@npm:^2.0.14":
version: 2.0.13 version: 2.0.14
resolution: "node-releases@npm:2.0.13" resolution: "node-releases@npm:2.0.14"
checksum: 2fb44bf70fc949d27f3a48a7fd1a9d1d603ddad4ccd091f26b3fb8b1da976605d919330d7388ccd55ca2ade0dc8b2e12841ba19ef249c8bb29bf82532d401af7 checksum: 199fc93773ae70ec9969bc6d5ac5b2bbd6eb986ed1907d751f411fef3ede0e4bfdb45ceb43711f8078bea237b6036db8b1bf208f6ff2b70c7d615afd157f3ab9
languageName: node languageName: node
linkType: hard linkType: hard
@ -13340,11 +13341,11 @@ __metadata:
linkType: hard linkType: hard
"prettier@npm:^3.0.0": "prettier@npm:^3.0.0":
version: 3.1.0 version: 3.1.1
resolution: "prettier@npm:3.1.0" resolution: "prettier@npm:3.1.1"
bin: bin:
prettier: bin/prettier.cjs prettier: bin/prettier.cjs
checksum: a45ea70aa97fde162ea4c4aba3dfc7859aa6a732a1db34458d9535dc3c2c16d3bc3fb5689e6cd76aa835562555303b02d9449fd2e15af3b73c8053557e25c5b6 checksum: facc944ba20e194ff4db765e830ffbcb642803381f0d2033ed397e79904fa4ccc877dc25ad68f42d36985c01d051c990ca1b905fb83d2d7d65fe69e4386fa1a3
languageName: node languageName: node
linkType: hard linkType: hard
@ -15792,62 +15793,62 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"stylelint-config-recommended-scss@npm:^13.1.0": "stylelint-config-recommended-scss@npm:^14.0.0":
version: 13.1.0 version: 14.0.0
resolution: "stylelint-config-recommended-scss@npm:13.1.0" resolution: "stylelint-config-recommended-scss@npm:14.0.0"
dependencies: dependencies:
postcss-scss: "npm:^4.0.9" postcss-scss: "npm:^4.0.9"
stylelint-config-recommended: "npm:^13.0.0" stylelint-config-recommended: "npm:^14.0.0"
stylelint-scss: "npm:^5.3.0" stylelint-scss: "npm:^6.0.0"
peerDependencies: peerDependencies:
postcss: ^8.3.3 postcss: ^8.3.3
stylelint: ^15.10.0 stylelint: ^16.0.2
peerDependenciesMeta: peerDependenciesMeta:
postcss: postcss:
optional: true optional: true
checksum: e07d0172c7936b4f644138e4129df2f187d297f1f96ce5865ab21ccd1c22caf94220f7caf9d6985e93e515de4c0356f6cb9c924d00df2eee5b3bc237f7e5bb48 checksum: 9ddc92e7a5fa131b41cee1ab1f69251934ca35c0e2803dc613329cdead7b8b27d8457048a63db29f61a1442e7cdef14207f88a3abce00ec53fdefe0d604f7de3
languageName: node languageName: node
linkType: hard linkType: hard
"stylelint-config-recommended@npm:^13.0.0": "stylelint-config-recommended@npm:^14.0.0":
version: 13.0.0 version: 14.0.0
resolution: "stylelint-config-recommended@npm:13.0.0" resolution: "stylelint-config-recommended@npm:14.0.0"
peerDependencies: peerDependencies:
stylelint: ^15.10.0 stylelint: ^16.0.0
checksum: 80420a1ab616e8637b66223f88c597388990d9991cd6a28b8372049b83329d893412f83029bb253a82b52387e497b62e042bc898064a2f22574b0d8921f01dd2 checksum: 4ad15c36e8c03291aa7bbe4b672ebfb0f46ab698e7580a0da8d29644046d102d7f31dbf00a2a6eab94b565c390c6fb0d5d528737b83ac3acf6dc2ef085a90b11
languageName: node languageName: node
linkType: hard linkType: hard
"stylelint-config-standard-scss@npm:^11.0.0": "stylelint-config-standard-scss@npm:^12.0.0":
version: 11.1.0 version: 12.0.0
resolution: "stylelint-config-standard-scss@npm:11.1.0" resolution: "stylelint-config-standard-scss@npm:12.0.0"
dependencies: dependencies:
stylelint-config-recommended-scss: "npm:^13.1.0" stylelint-config-recommended-scss: "npm:^14.0.0"
stylelint-config-standard: "npm:^34.0.0" stylelint-config-standard: "npm:^35.0.0"
peerDependencies: peerDependencies:
postcss: ^8.3.3 postcss: ^8.3.3
stylelint: ^15.10.0 stylelint: ^16.0.2
peerDependenciesMeta: peerDependenciesMeta:
postcss: postcss:
optional: true optional: true
checksum: 22d00e75c1eacce9883fd48c3d67b1107b0e39d7d86e9f73deaa332b11c39a9678c947ae2c34cd5159a452ec9a857694ed58b5a851087480d3c9a66dab629415 checksum: 7f3ccfb4175f9c50b69d30ca35a97887008c5ba493dbe7d5bce0b57b1eafd21b268177b82404368e7780600077cba784f98e1046671724be3b29a00c6a7913a4
languageName: node languageName: node
linkType: hard linkType: hard
"stylelint-config-standard@npm:^34.0.0": "stylelint-config-standard@npm:^35.0.0":
version: 34.0.0 version: 35.0.0
resolution: "stylelint-config-standard@npm:34.0.0" resolution: "stylelint-config-standard@npm:35.0.0"
dependencies: dependencies:
stylelint-config-recommended: "npm:^13.0.0" stylelint-config-recommended: "npm:^14.0.0"
peerDependencies: peerDependencies:
stylelint: ^15.10.0 stylelint: ^16.0.0
checksum: 2494468af2359490b6ebb9723d9653f9e31db3a0772b8d9f0e081018b0079ef84ae6f90dcf94c879a3c374f299e334941e3dcff1afb603c2284d3103085b71fb checksum: 791fbc26cc3029ce3c2423a643e903545b5e4cd605251b18f0ce790bac6fbaaf380469845c1ff45f4e320126af9f8a9dc1ca85d0df9274277ae60da91e81895b
languageName: node languageName: node
linkType: hard linkType: hard
"stylelint-scss@npm:^5.3.0": "stylelint-scss@npm:^6.0.0":
version: 5.3.1 version: 6.0.0
resolution: "stylelint-scss@npm:5.3.1" resolution: "stylelint-scss@npm:6.0.0"
dependencies: dependencies:
known-css-properties: "npm:^0.29.0" known-css-properties: "npm:^0.29.0"
postcss-media-query-parser: "npm:^0.2.3" postcss-media-query-parser: "npm:^0.2.3"
@ -15855,8 +15856,8 @@ __metadata:
postcss-selector-parser: "npm:^6.0.13" postcss-selector-parser: "npm:^6.0.13"
postcss-value-parser: "npm:^4.2.0" postcss-value-parser: "npm:^4.2.0"
peerDependencies: peerDependencies:
stylelint: ^14.5.1 || ^15.0.0 stylelint: ^16.0.2
checksum: 5dfed5f9ac9812cd2ac6ef0272c720dee0326aaaee2998315a23bdcd71b8f04427f29cad634793eea2b45984182e20f03e90d43501e8e4d55bc956f80e2de477 checksum: f5e971d19ef6879ae5c18cb8fba8033fe7928f241178e6afd80357cc080d2feddfd6f7fe564aaa696008aa10345df5885d9a4471c926b3e266088e015927782e
languageName: node languageName: node
linkType: hard linkType: hard
@ -16530,22 +16531,22 @@ __metadata:
linkType: hard linkType: hard
"typescript@npm:5, typescript@npm:^5.0.4": "typescript@npm:5, typescript@npm:^5.0.4":
version: 5.3.2 version: 5.3.3
resolution: "typescript@npm:5.3.2" resolution: "typescript@npm:5.3.3"
bin: bin:
tsc: bin/tsc tsc: bin/tsc
tsserver: bin/tsserver tsserver: bin/tsserver
checksum: d7dbe1fbe19039e36a65468ea64b5d338c976550394ba576b7af9c68ed40c0bc5d12ecce390e4b94b287a09a71bd3229f19c2d5680611f35b7c53a3898791159 checksum: e33cef99d82573624fc0f854a2980322714986bc35b9cb4d1ce736ed182aeab78e2cb32b385efa493b2a976ef52c53e20d6c6918312353a91850e2b76f1ea44f
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@npm%3A5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>": "typescript@patch:typescript@npm%3A5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>":
version: 5.3.2 version: 5.3.3
resolution: "typescript@patch:typescript@npm%3A5.3.2#optional!builtin<compat/typescript>::version=5.3.2&hash=e012d7" resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin<compat/typescript>::version=5.3.3&hash=e012d7"
bin: bin:
tsc: bin/tsc tsc: bin/tsc
tsserver: bin/tsserver tsserver: bin/tsserver
checksum: 73c8bad74e732d93211c9d77f28b03307e2f5fc6a0afc73f4b783261ab567686a16d6ae958bdaef383a00be1b0b8c8b6741dd6ca3d13af4963fa7e47456d49c7 checksum: 1d0a5f4ce496c42caa9a30e659c467c5686eae15d54b027ee7866744952547f1be1262f2d40de911618c242b510029d51d43ff605dba8fb740ec85ca2d3f9500
languageName: node languageName: node
linkType: hard linkType: hard
@ -17782,8 +17783,8 @@ __metadata:
linkType: hard linkType: hard
"ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.14.2": "ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.14.2":
version: 8.14.2 version: 8.15.1
resolution: "ws@npm:8.14.2" resolution: "ws@npm:8.15.1"
peerDependencies: peerDependencies:
bufferutil: ^4.0.1 bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2" utf-8-validate: ">=5.0.2"
@ -17792,7 +17793,7 @@ __metadata:
optional: true optional: true
utf-8-validate: utf-8-validate:
optional: true optional: true
checksum: 35b4c2da048b8015c797fd14bcb5a5766216ce65c8a5965616a5440ca7b6c3681ee3cbd0ea0c184a59975556e9d58f2002abf8485a14d11d3371770811050a16 checksum: 9964360dd5ab35c7376bd7c4295a3c8bd44ea0838c9413742548a6fb3ec371fc6c18552d5b8e76bdc21536db1909765612815bae072674b5ec69971605395a96
languageName: node languageName: node
linkType: hard linkType: hard