From 213c87ae595cc1ddcb618516106712b0aae789bd Mon Sep 17 00:00:00 2001 From: Evan Paterakis Date: Tue, 27 Feb 2024 12:46:58 +0200 Subject: [PATCH 01/18] Fix filters title and keywords overflow (#29396) --- app/javascript/styles/mastodon/admin.scss | 1 + app/javascript/styles/mastodon/forms.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 5625cdd5ecce13..fcd630c23c9ecc 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -1044,6 +1044,7 @@ a.name-tag, display: flex; justify-content: space-between; margin-bottom: 0; + word-break: break-word; } &__permissions { diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 555d43cc1c4466..3ac5c3df959298 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -1078,6 +1078,7 @@ code { &__type { color: $darker-text-color; + word-break: break-word; } } From 9fa7338b6e5921e184cd23c21df40a08940d03de Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 27 Feb 2024 05:48:38 -0500 Subject: [PATCH 02/18] Use `github` reporter on `haml-lint` runs on CI (#29375) --- .github/workflows/lint-haml.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml index 8dcab845ee085c..25615b720d45ad 100644 --- a/.github/workflows/lint-haml.yml +++ b/.github/workflows/lint-haml.yml @@ -36,4 +36,4 @@ jobs: - name: Run haml-lint run: | echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json" - bundle exec haml-lint + bundle exec haml-lint --reporter github From 90573c3abbc0783c25cb4e3aa8c298e10259cb57 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 27 Feb 2024 12:41:19 +0100 Subject: [PATCH 03/18] Change behavior of privacy dropdown to only change value on validation (#29406) --- .../compose/components/language_dropdown.jsx | 1 + .../compose/components/privacy_dropdown.jsx | 124 +---------------- .../components/privacy_dropdown_menu.jsx | 128 ++++++++++++++++++ 3 files changed, 131 insertions(+), 122 deletions(-) create mode 100644 app/javascript/mastodon/features/compose/components/privacy_dropdown_menu.jsx diff --git a/app/javascript/mastodon/features/compose/components/language_dropdown.jsx b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx index 85057799be2fa7..c3bd908a4ebe06 100644 --- a/app/javascript/mastodon/features/compose/components/language_dropdown.jsx +++ b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx @@ -141,6 +141,7 @@ class LanguageDropdownMenu extends PureComponent { case 'Escape': onClose(); break; + case ' ': case 'Enter': this.handleClick(e); break; diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx index 82f902738891d8..071f0a6fabf399 100644 --- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx +++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx @@ -5,16 +5,16 @@ import { injectIntl, defineMessages } from 'react-intl'; import classNames from 'classnames'; -import { supportsPassiveEvents } from 'detect-passive-events'; import Overlay from 'react-overlays/Overlay'; import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; -import InfoIcon from '@/material-icons/400-24px/info.svg?react'; import LockIcon from '@/material-icons/400-24px/lock.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react'; import { Icon } from 'mastodon/components/icon'; +import { PrivacyDropdownMenu } from './privacy_dropdown_menu'; + const messages = defineMessages({ public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' }, @@ -28,126 +28,6 @@ const messages = defineMessages({ unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.' }, }); -const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true; - -class PrivacyDropdownMenu extends PureComponent { - - static propTypes = { - style: PropTypes.object, - items: PropTypes.array.isRequired, - value: PropTypes.string.isRequired, - onClose: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - }; - - handleDocumentClick = e => { - if (this.node && !this.node.contains(e.target)) { - this.props.onClose(); - e.stopPropagation(); - } - }; - - handleKeyDown = e => { - const { items } = this.props; - const value = e.currentTarget.getAttribute('data-index'); - const index = items.findIndex(item => { - return (item.value === value); - }); - let element = null; - - switch(e.key) { - case 'Escape': - this.props.onClose(); - break; - case 'Enter': - this.handleClick(e); - break; - case 'ArrowDown': - element = this.node.childNodes[index + 1] || this.node.firstChild; - break; - case 'ArrowUp': - element = this.node.childNodes[index - 1] || this.node.lastChild; - break; - case 'Tab': - if (e.shiftKey) { - element = this.node.childNodes[index - 1] || this.node.lastChild; - } else { - element = this.node.childNodes[index + 1] || this.node.firstChild; - } - break; - case 'Home': - element = this.node.firstChild; - break; - case 'End': - element = this.node.lastChild; - break; - } - - if (element) { - element.focus(); - this.props.onChange(element.getAttribute('data-index')); - e.preventDefault(); - e.stopPropagation(); - } - }; - - handleClick = e => { - const value = e.currentTarget.getAttribute('data-index'); - - e.preventDefault(); - - this.props.onClose(); - this.props.onChange(value); - }; - - componentDidMount () { - document.addEventListener('click', this.handleDocumentClick, { capture: true }); - document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); - if (this.focusedItem) this.focusedItem.focus({ preventScroll: true }); - } - - componentWillUnmount () { - document.removeEventListener('click', this.handleDocumentClick, { capture: true }); - document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - setRef = c => { - this.node = c; - }; - - setFocusRef = c => { - this.focusedItem = c; - }; - - render () { - const { style, items, value } = this.props; - - return ( -
- {items.map(item => ( -
-
- -
- -
- {item.text} - {item.meta} -
- - {item.extra && ( -
- -
- )} -
- ))} -
- ); - } - -} - class PrivacyDropdown extends PureComponent { static propTypes = { diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown_menu.jsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown_menu.jsx new file mode 100644 index 00000000000000..1a5ff1fa803b03 --- /dev/null +++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown_menu.jsx @@ -0,0 +1,128 @@ +import PropTypes from 'prop-types'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import classNames from 'classnames'; + +import { supportsPassiveEvents } from 'detect-passive-events'; + +import InfoIcon from '@/material-icons/400-24px/info.svg?react'; +import { Icon } from 'mastodon/components/icon'; + +const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true; + +export const PrivacyDropdownMenu = ({ style, items, value, onClose, onChange }) => { + const nodeRef = useRef(null); + const focusedItemRef = useRef(null); + const [currentValue, setCurrentValue] = useState(value); + + const handleDocumentClick = useCallback((e) => { + if (nodeRef.current && !nodeRef.current.contains(e.target)) { + onClose(); + e.stopPropagation(); + } + }, [nodeRef, onClose]); + + const handleClick = useCallback((e) => { + const value = e.currentTarget.getAttribute('data-index'); + + e.preventDefault(); + + onClose(); + onChange(value); + }, [onClose, onChange]); + + const handleKeyDown = useCallback((e) => { + const value = e.currentTarget.getAttribute('data-index'); + const index = items.findIndex(item => (item.value === value)); + + let element = null; + + switch (e.key) { + case 'Escape': + onClose(); + break; + case ' ': + case 'Enter': + handleClick(e); + break; + case 'ArrowDown': + element = nodeRef.current.childNodes[index + 1] || nodeRef.current.firstChild; + break; + case 'ArrowUp': + element = nodeRef.current.childNodes[index - 1] || nodeRef.current.lastChild; + break; + case 'Tab': + if (e.shiftKey) { + element = nodeRef.current.childNodes[index + 1] || nodeRef.current.firstChild; + } else { + element = nodeRef.current.childNodes[index - 1] || nodeRef.current.lastChild; + } + break; + case 'Home': + element = nodeRef.current.firstChild; + break; + case 'End': + element = nodeRef.current.lastChild; + break; + } + + if (element) { + element.focus(); + setCurrentValue(element.getAttribute('data-index')); + e.preventDefault(); + e.stopPropagation(); + } + }, [nodeRef, items, onClose, handleClick, setCurrentValue]); + + useEffect(() => { + document.addEventListener('click', handleDocumentClick, { capture: true }); + document.addEventListener('touchend', handleDocumentClick, listenerOptions); + focusedItemRef.current?.focus({ preventScroll: true }); + + return () => { + document.removeEventListener('click', handleDocumentClick, { capture: true }); + document.removeEventListener('touchend', handleDocumentClick, listenerOptions); + }; + }, [handleDocumentClick]); + + return ( +
    + {items.map(item => ( +
  • +
    + +
    + +
    + {item.text} + {item.meta} +
    + + {item.extra && ( +
    + +
    + )} +
  • + ))} +
+ ); +}; + +PrivacyDropdownMenu.propTypes = { + style: PropTypes.object, + items: PropTypes.array.isRequired, + value: PropTypes.string.isRequired, + onClose: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, +}; From 54e3a82f1d5248ff91a297d6d06de9f86e9cd208 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 12:48:42 +0100 Subject: [PATCH 04/18] Update dependency thor to v1.3.1 (#29421) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 090697e709968d..076cf915d05bfd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -744,7 +744,7 @@ GEM terrapin (1.0.1) climate_control test-prof (1.3.1) - thor (1.3.0) + thor (1.3.1) tilt (2.3.0) timeout (0.4.1) tpm-key_attestation (0.12.0) From 9e78129e6e45e9485f567a88cb4b3dbc0b25a7d2 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 27 Feb 2024 06:50:21 -0500 Subject: [PATCH 05/18] Use "cacheable response" shared example in more places (#29419) --- spec/controllers/custom_css_controller_spec.rb | 13 +------------ .../instance_actors_controller_spec.rb | 13 ++++--------- spec/controllers/manifests_controller_spec.rb | 13 +------------ spec/controllers/tags_controller_spec.rb | 16 ++-------------- spec/support/examples/cache.rb | 2 +- 5 files changed, 9 insertions(+), 48 deletions(-) diff --git a/spec/controllers/custom_css_controller_spec.rb b/spec/controllers/custom_css_controller_spec.rb index 99d36d21b9862b..405fa0bcf35c53 100644 --- a/spec/controllers/custom_css_controller_spec.rb +++ b/spec/controllers/custom_css_controller_spec.rb @@ -14,17 +14,6 @@ expect(response).to have_http_status(200) end - it 'returns public cache control header' do - expect(response.headers['Cache-Control']).to include('public') - end - - it 'does not set cookies' do - expect(response.cookies).to be_empty - expect(response.headers['Set-Cookies']).to be_nil - end - - it 'does not set sessions' do - expect(session).to be_empty - end + it_behaves_like 'cacheable response' end end diff --git a/spec/controllers/instance_actors_controller_spec.rb b/spec/controllers/instance_actors_controller_spec.rb index be1eefa7b22747..70aaff9d65b098 100644 --- a/spec/controllers/instance_actors_controller_spec.rb +++ b/spec/controllers/instance_actors_controller_spec.rb @@ -12,23 +12,18 @@ get :show, params: { format: format } end - it 'returns http success with correct media type, headers, and session values' do + it 'returns http success with correct media type and body' do expect(response) .to have_http_status(200) .and have_attributes( - media_type: eq('application/activity+json'), - cookies: be_empty + media_type: eq('application/activity+json') ) - expect(response.headers) - .to include('Cache-Control' => include('public')) - .and not_include('Set-Cookies') - - expect(session).to be_empty - expect(body_as_json) .to include(:id, :type, :preferredUsername, :inbox, :publicKey, :inbox, :outbox, :url) end + + it_behaves_like 'cacheable response' end before do diff --git a/spec/controllers/manifests_controller_spec.rb b/spec/controllers/manifests_controller_spec.rb index d0699c438b81a1..9279fae0245561 100644 --- a/spec/controllers/manifests_controller_spec.rb +++ b/spec/controllers/manifests_controller_spec.rb @@ -14,17 +14,6 @@ expect(response).to have_http_status(200) end - it 'returns public cache control header' do - expect(response.headers['Cache-Control']).to include('public') - end - - it 'does not set cookies' do - expect(response.cookies).to be_empty - expect(response.headers['Set-Cookies']).to be_nil - end - - it 'does not set sessions' do - expect(session).to be_empty - end + it_behaves_like 'cacheable response' end end diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb index d41e707d432302..2bb0c8de3b05d9 100644 --- a/spec/controllers/tags_controller_spec.rb +++ b/spec/controllers/tags_controller_spec.rb @@ -20,13 +20,7 @@ expect(response).to have_http_status(200) end - it 'returns Vary header' do - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - end - - it 'returns public Cache-Control header' do - expect(response.headers['Cache-Control']).to include 'public' - end + it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' end context 'when requested as JSON' do @@ -36,13 +30,7 @@ expect(response).to have_http_status(200) end - it 'returns Vary header' do - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - end - - it 'returns public Cache-Control header' do - expect(response.headers['Cache-Control']).to include 'public' - end + it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' end end diff --git a/spec/support/examples/cache.rb b/spec/support/examples/cache.rb index afbee66b2d357a..60e522f4269311 100644 --- a/spec/support/examples/cache.rb +++ b/spec/support/examples/cache.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true shared_examples 'cacheable response' do |expects_vary: false| - it 'sets correct cache and vary headers and does not set cookies or session' do + it 'sets correct cache and vary headers and does not set cookies or session', :aggregate_failures do expect(response.cookies).to be_empty expect(response.headers['Set-Cookies']).to be_nil From 76d256138e6029d848da7cfdf5b8682832c44033 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 27 Feb 2024 06:52:37 -0500 Subject: [PATCH 06/18] Wrap media attachment size calculation in `COALESCE` (#29415) --- .../admin/metrics/measure/instance_media_attachments_measure.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb index 2d4b5f56b01e91..1d2dbbe4146a86 100644 --- a/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb +++ b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb @@ -50,7 +50,7 @@ def sql_query_string WHERE date_trunc('day', media_attachments.created_at)::date = axis.period AND #{account_domain_sql(params[:include_subdomains])} ) - SELECT SUM(size) FROM new_media_attachments + SELECT COALESCE(SUM(size), 0) FROM new_media_attachments ) AS value FROM ( SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period From bc4c5ed91883439e5de9ef034b6875c36f4b4241 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:53:53 +0100 Subject: [PATCH 07/18] New Crowdin Translations (automated) (#29423) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/nn.json | 30 ++++++++++++++++--------- config/locales/devise.nn.yml | 19 ++++++++-------- config/locales/nn.yml | 22 +++++++++--------- config/locales/simple_form.nn.yml | 18 ++++++++------- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 2118eb57398f7c..3cc537f54f0121 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -40,7 +40,7 @@ "account.following_counter": "{count, plural, one {Fylgjer {counter}} other {Fylgjer {counter}}}", "account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.", "account.go_to_profile": "Gå til profil", - "account.hide_reblogs": "Skjul framhevingar frå @{name}", + "account.hide_reblogs": "Gøym framhevingar frå @{name}", "account.in_memoriam": "Til minne om.", "account.joined_short": "Vart med", "account.languages": "Endre språktingingar", @@ -113,7 +113,7 @@ "column.community": "Lokal tidsline", "column.direct": "Private omtaler", "column.directory": "Sjå gjennom profilar", - "column.domain_blocks": "Skjulte domene", + "column.domain_blocks": "Blokkerte domene", "column.favourites": "Favorittar", "column.firehose": "Tidslinjer", "column.follow_requests": "Fylgjeførespurnadar", @@ -124,7 +124,7 @@ "column.pins": "Festa tut", "column.public": "Samla tidsline", "column_back_button.label": "Attende", - "column_header.hide_settings": "Gøym innstillingar", + "column_header.hide_settings": "Gøym innstillingane", "column_header.moveLeft_settings": "Flytt kolonne til venstre", "column_header.moveRight_settings": "Flytt kolonne til høgre", "column_header.pin": "Fest", @@ -171,14 +171,14 @@ "confirmations.delete_list.message": "Er du sikker på at du vil sletta denne lista for alltid?", "confirmations.discard_edit_media.confirm": "Forkast", "confirmations.discard_edit_media.message": "Du har ulagra endringar i mediaskildringa eller førehandsvisinga. Vil du forkasta dei likevel?", - "confirmations.domain_block.confirm": "Skjul alt frå domenet", + "confirmations.domain_block.confirm": "Blokker heile domenet", "confirmations.domain_block.message": "Er du heilt, heilt sikker på at du vil skjula heile {domain}? I dei fleste tilfelle er det godt nok og føretrekt med nokre få målretta blokkeringar eller målbindingar. Du kjem ikkje til å sjå innhald frå domenet i fødererte tidsliner eller i varsla dine. Fylgjarane dine frå domenet vert fjerna.", "confirmations.edit.confirm": "Rediger", "confirmations.edit.message": "Å redigera no vil overskriva den meldinga du er i ferd med å skriva. Er du sikker på at du vil halda fram?", "confirmations.logout.confirm": "Logg ut", "confirmations.logout.message": "Er du sikker på at du vil logga ut?", "confirmations.mute.confirm": "Målbind", - "confirmations.mute.explanation": "Dette vil skjula innlegg som kjem frå og som nemner dei, men vil framleis la dei sjå innlegga dine og fylgje deg.", + "confirmations.mute.explanation": "Dette vil gøyma innlegga deira og innlegg som nemner dei, men dei vil framleis kunna sjå innlegga dine og fylgja deg.", "confirmations.mute.message": "Er du sikker på at du vil målbinda {name}?", "confirmations.redraft.confirm": "Slett & skriv på nytt", "confirmations.redraft.message": "Er du sikker på at du vil sletta denne statusen og skriva han på nytt? Då misser du favorittar og framhevingar, og svar til det opprinnelege innlegget vert foreldrelause.", @@ -230,7 +230,7 @@ "empty_column.bookmarked_statuses": "Du har ikkje lagra noko bokmerke enno. Når du set bokmerke på eit innlegg, dukkar det opp her.", "empty_column.community": "Den lokale tidslina er tom. Skriv noko offentleg å få ballen til å rulle!", "empty_column.direct": "Du har ingen private omtaler enda. Etter du har sendt eller mottatt en, så vil den dukke opp her.", - "empty_column.domain_blocks": "Det er ingen skjulte domene til no.", + "empty_column.domain_blocks": "Det er ingen blokkerte domene enno.", "empty_column.explore_statuses": "Ingenting er i støytet nett no. Prøv igjen seinare!", "empty_column.favourited_statuses": "Du har ingen favoritt-statusar ennå. Når du merkjer ein som favoritt, dukkar han opp her.", "empty_column.favourites": "Ingen har merkt denne statusen som favoritt enno. Når nokon gjer det, dukkar dei opp her.", @@ -277,7 +277,13 @@ "follow_request.authorize": "Autoriser", "follow_request.reject": "Avvis", "follow_requests.unlocked_explanation": "Sjølv om kontoen din ikkje er låst tenkte dei som driv {domain} at du kanskje ville gå gjennom førespurnadar frå desse kontoane manuelt.", + "follow_suggestions.curated_suggestion": "Utvalt av staben", "follow_suggestions.dismiss": "Ikkje vis igjen", + "follow_suggestions.hints.featured": "Denne profilen er handplukka av folka på {domain}.", + "follow_suggestions.hints.friends_of_friends": "Denne profilen er populær hjå dei du fylgjer.", + "follow_suggestions.hints.most_followed": "Mange på {domain} fylgjer denne profilen.", + "follow_suggestions.hints.most_interactions": "Denne profilen har nyss fått mykje merksemd på {domain}.", + "follow_suggestions.hints.similar_to_recently_followed": "Denne profilen liknar på dei andre profilane du har fylgt i det siste.", "follow_suggestions.personalized_suggestion": "Personleg forslag", "follow_suggestions.popular_suggestion": "Populært forslag", "follow_suggestions.view_all": "Vis alle", @@ -395,7 +401,7 @@ "media_gallery.toggle_visible": "{number, plural, one {Skjul bilete} other {Skjul bilete}}", "moved_to_account_banner.text": "Kontoen din, {disabledAccount} er for tida deaktivert fordi du har flytta til {movedToAccount}.", "mute_modal.duration": "Varigheit", - "mute_modal.hide_notifications": "Skjul varsel frå denne brukaren?", + "mute_modal.hide_notifications": "Gøym varsel frå denne brukaren?", "mute_modal.indefinite": "På ubestemt tid", "navigation_bar.about": "Om", "navigation_bar.advanced_interface": "Opne i avansert nettgrensesnitt", @@ -479,7 +485,8 @@ "onboarding.follows.empty": "Me kan ikkje visa deg nokon resultat no. Du kan prøva å søkja eller bla gjennom utforsk-sida for å finna folk å fylgja, eller du kan prøva att seinare.", "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.profile.discoverable": "Gjør min profil synlig", + "onboarding.profile.discoverable": "Gjer profilen min synleg", + "onboarding.profile.discoverable_hint": "Når du vel å gjera profilen din synleg på Mastodon, vil innlegga dine syna i søkjeresultat og populære innlegg, og profilen din kan bli føreslegen for folk med liknande interesser som deg.", "onboarding.profile.display_name": "Synleg namn", "onboarding.profile.display_name_hint": "Det fulle namnet eller kallenamnet ditt…", "onboarding.profile.lead": "Du kan alltid fullføra dette seinare i innstillingane, og der er det endå fleire tilpassingsalternativ.", @@ -528,11 +535,12 @@ "privacy.private.short": "Følgjarar", "privacy.public.long": "Kven som helst på og av Mastodon", "privacy.public.short": "Offentleg", + "privacy.unlisted.additional": "Dette er akkurat som offentleg, bortsett frå at innlegga ikkje dukkar opp i direktestraumar eller merkelappar, i oppdagingar eller Mastodon-søk, sjølv om du har sagt ja til at kontoen skal vera synleg.", "privacy.unlisted.long": "Færre algoritmiske fanfarar", "privacy.unlisted.short": "Stille offentleg", "privacy_policy.last_updated": "Sist oppdatert {date}", "privacy_policy.title": "Personvernsreglar", - "recommended": "Anbefalt", + "recommended": "Tilrådd", "refresh": "Oppdater", "regeneration_indicator.label": "Lastar…", "regeneration_indicator.sublabel": "Heimetidslina di vert førebudd!", @@ -605,7 +613,7 @@ "search.quick_action.status_search": "Innlegg som samsvarer med {x}", "search.search_or_paste": "Søk eller lim inn URL", "search_popout.full_text_search_disabled_message": "Ikkje tilgjengeleg på {domain}.", - "search_popout.full_text_search_logged_out_message": "Bare tilgjengelig når man er logget inn.", + "search_popout.full_text_search_logged_out_message": "Berre tilgjengeleg når du er logga inn.", "search_popout.language_code": "ISO-språkkode", "search_popout.options": "Søkjealternativ", "search_popout.quick_actions": "Hurtighandlinger", @@ -654,7 +662,7 @@ "status.load_more": "Last inn meir", "status.media.open": "Klikk for å opne", "status.media.show": "Klikk for å vise", - "status.media_hidden": "Medium gøymd", + "status.media_hidden": "Mediet er gøymt", "status.mention": "Nemn @{name}", "status.more": "Meir", "status.mute": "Målbind @{name}", diff --git a/config/locales/devise.nn.yml b/config/locales/devise.nn.yml index 96920d42b58622..01d6e5a468d390 100644 --- a/config/locales/devise.nn.yml +++ b/config/locales/devise.nn.yml @@ -12,6 +12,7 @@ nn: last_attempt: Du har eitt forsøk igjen før kontoen din vert låst. locked: Kontoen din er låst. not_found_in_database: Ugyldig %{authentication_keys} eller passord. + omniauth_user_creation_failure: Greidde ikkje laga konto for denne identiteten. pending: Kontoen din er vert gjennomgått enno. timeout: Økta di er utgått. Logg inn omatt for å halde fram. unauthenticated: Du må logge inn eller registere deg før du kan halde fram. @@ -47,19 +48,19 @@ nn: subject: 'Mastodon: Instuksjonar for å endra passord' title: Attstilling av passord two_factor_disabled: - explanation: Innlogging er nå mulig med kun e-postadresse og passord. + explanation: No kan du logga inn med berre epostadresse og passord. subject: 'Mastodon: To-faktor-autentisering deaktivert' - subtitle: To-faktor autentisering for din konto har blitt deaktivert. + subtitle: Tofaktorinnlogging for denne kontoen er skrudd av. title: 2FA deaktivert two_factor_enabled: - explanation: En token generert av den sammenkoblede TOTP-appen vil være påkrevd for innlogging. + explanation: Du treng ein kode frå den tilkopla tofaktor-appen din for å logga inn. subject: 'Mastodon: To-faktor-autentisering aktivert' - subtitle: Tofaktorautentisering er aktivert for din konto. + subtitle: Tofaktorpålogging er skrudd på for kontoen din. title: 2FA aktivert two_factor_recovery_codes_changed: explanation: Dei førre gjenopprettingskodane er ugyldige og nye er genererte. subject: 'Mastodon: To-faktor-gjenopprettingskodar har vorte genererte på nytt' - subtitle: De forrige gjenopprettingskodene er gjort ugyldige og nye er generert. + subtitle: Dei førre innloggingskodane er ikkje gyldige lenger, og nye kodar er laga. title: 2FA-gjenopprettingskodane er endra unlock_instructions: subject: 'Mastodon: Instruksjonar for å opne kontoen igjen' @@ -73,13 +74,13 @@ nn: subject: 'Mastodon: Sikkerheitsnøkkel sletta' title: Ein av sikkerheitsnøklane dine har blitt sletta webauthn_disabled: - explanation: Autentisering med sikkerhetsnøkler er deaktivert for kontoen din. - extra: Innlogging er nå mulig med kun tilgangstoken generert av den sammenkoblede TOTP-appen. + explanation: Innlogging med tryggingsnykjel er skrudd av for kontoen din. + extra: No kan du logga inn med berre kodane som er laga av den tilkopla tofaktor-appen din. subject: 'Mastodon: Autentisering med sikkerheitsnøklar vart skrudd av' title: Sikkerheitsnøklar deaktivert webauthn_enabled: - explanation: Sikkerhetsnøkkelautentisering har blitt aktivert for kontoen din. - extra: Sikkerhetsnøkkelen din kan nå bli brukt for innlogging. + explanation: Innlogging med tryggingsnyklar er skrudd på for kontoen din. + extra: No kan du bruka tryggingsnykjelen din for å logga inn. subject: 'Mastodon: Sikkerheitsnøkkelsautentisering vart skrudd på' title: Sikkerheitsnøklar aktivert omniauth_callbacks: diff --git a/config/locales/nn.yml b/config/locales/nn.yml index b1ae928997bb79..1524b6f7c13d2a 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -31,7 +31,7 @@ nn: created_msg: Moderatormerknad er laga! destroyed_msg: Moderatormerknad er utsletta! accounts: - add_email_domain_block: Gøym e-postdomene + add_email_domain_block: Blokker e-postdomene approve: Godtak approved_msg: Godkjende %{username} sin registreringssøknad are_you_sure: Er du sikker? @@ -767,13 +767,15 @@ nn: disabled: Til ingen users: Til lokale brukarar som er logga inn registrations: + moderation_recommandation: Pass på at du har mange og kjappe redaktørar og moderatorar på laget ditt før du opnar for allmenn registrering! preamble: Kontroller kven som kan oppretta konto på tenaren din. title: Registreringar registrations_mode: modes: - approved: Godkjenning kreves for påmelding + approved: Godkjenning krevst for å registrera seg none: Ingen kan melda seg inn open: Kven som helst kan melda seg inn + warning_hint: Me rår til at du bruker "Godkjenning krevst for å registrera seg" viss du ikkje er sikker på at moderatorane kan handtera søppel og illmeinte registreringar kvikt. security: authorized_fetch: Krev autentisering frå fødererte tenarar authorized_fetch_hint: Krav om autentisering frå fødererte tenarar gjer det mogleg med strengare handheving av blokkering, både på brukar- og tenar-nivå. Likevel, dette har ein kostnad når det gjeld yting, reduserer rekkevidda til svara dine og kan medføra kompabilitetsproblem med enkelte fødererte tenester. Dette vil heller ikkje hindra dei som verkeleg vil i å henta dei offentlege innlegga eller kontoane dine. @@ -1450,7 +1452,7 @@ nn: moderation: title: Moderasjon move_handler: - carry_blocks_over_text: Denne brukaren flytta frå %{acct}, som du gøymde. + carry_blocks_over_text: Denne brukaren flytta frå %{acct}, som du hadde blokkert. carry_mutes_over_text: Denne brukeren flyttet fra %{acct}, som du hadde dempet. copy_account_note_text: 'Denne brukeren flyttet fra %{acct}, her var dine tidligere notater om dem:' navigation: @@ -1537,7 +1539,7 @@ nn: privacy: hint_html: "Tilpass korleis du vil at andre skal finna profilen og innlegga dine. Mastodon har fleire funksjonar du kan ta i bruk for å få kontakt med eit større publikum. Sjå gjerne gjennom innstillingane slik at du er sikker på at dei passar til deg og din bruk." privacy: Personvern - privacy_hint_html: Ha kontroll over kor mykje du vil dela. Folk finn interessante profilar og fine appar ved å sjå gjennom kva andre fylgjer og kva appar dei legg ut innlegg med, men det kan henda du vil gøyma desse opplysingane. + privacy_hint_html: Kontroller kor mykje du vil dela. Folk finn interessante profilar og fine appar ved å sjå gjennom kva andre fylgjer og kva appar dei legg ut innlegg med, men det kan henda du vil gøyma desse opplysingane. reach: Nå andre reach_hint_html: Hald styring med om du vil at andre skal kunna oppdaga og fylgja deg. Vil du at innlegga dine skal stå på Utforsk-sida? Vil du at andre skal sjå deg i tilrådingane for kven dei skal fylgja? Vil du ta imot nye fylgjarar automatisk, eller vil du kontrollera kvar einskild fylgjar? search: Søk @@ -1550,8 +1552,8 @@ nn: limit_reached: Grensen for forskjellige reaksjoner nådd unrecognized_emoji: er ikke en gjenkjent emoji redirects: - prompt: Hvis du stoler på denne lenken, så trykk på den for å fortsette. - title: Du forlater %{instance}. + prompt: Viss du stolar på denne lenka, klikkar du på ho for å halda fram. + title: No forlèt du %{instance}. relationships: activity: Kontoaktivitet confirm_follow_selected_followers: Er du sikker på at du ynskjer å fylgja dei valde fylgjarane? @@ -1781,7 +1783,7 @@ nn: webauthn: Sikkerhetsnøkler user_mailer: appeal_approved: - action: Kontoinnstillinger + action: Kontoinnstillingar explanation: Apellen på prikken mot din kontor på %{strike_date} som du la inn på %{appeal_date} har blitt godkjend. Din konto er nok ein gong i god stand. subject: Din klage fra %{date} er godkjent subtitle: Kontoen din er tilbake i god stand. @@ -1789,11 +1791,11 @@ nn: appeal_rejected: explanation: Klagen på advarselen mot din konto den %{strike_date} som du sendte inn den %{appeal_date} har blitt avvist. subject: Din klage fra %{date} er avvist - subtitle: Anken din har blitt avvist. + subtitle: Klaga di vart avvist. title: Anke avvist backup_ready: - explanation: Du etterspurte en fullstendig sikkerhetskopi av din Mastodon-konto. - extra: Den er nå klar for nedlasting! + explanation: Du ba om ein fullstendig tryggingskopi av Mastodon-kontoen din. + extra: No kan du lasta han ned! subject: Arkivet ditt er klart til å lastes ned title: Nedlasting av arkiv failed_2fa: diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml index 1991cb2dbb941f..98cc372be77d29 100644 --- a/config/locales/simple_form.nn.yml +++ b/config/locales/simple_form.nn.yml @@ -39,12 +39,14 @@ nn: text: Ei åtvaring kan kun ankast ein gong defaults: autofollow: Folk som lagar ein konto gjennom innbydinga fylgjer deg automatisk + avatar: WEBP, PNG, GIF eller JPG. Maks %{size}. Blir forminska til %{dimensions}pkt bot: Denne kontoen utfører i hovedsak automatiserte handlinger og blir kanskje ikke holdt øye med context: En eller flere sammenhenger der filteret skal gjelde current_password: For sikkerhetsgrunner, vennligst oppgi passordet til den nåværende bruker current_username: Skriv inn brukarnamnet til den noverande kontoen for å stadfesta digest: Kun sendt etter en lang periode med inaktivitet og bare dersom du har mottatt noen personlige meldinger mens du var borte email: Du får snart ein stadfestings-e-post + header: WEBP, PNG, GIF eller JPG. Maks %{size}. Blir forminska til %{dimensions}pkt inbox_url: Kopier URLen fra forsiden til overgangen du vil bruke irreversible: Filtrerte tut vil verta borte for evig, sjølv om filteret vert fjerna seinare locale: Språket til brukargrensesnittet, e-postar og push-varsel @@ -53,8 +55,8 @@ nn: scopes: API-ane som programmet vil få tilgjenge til. Ettersom du vel eit toppnivåomfang tarv du ikkje velja einskilde API-ar. setting_aggregate_reblogs: Ikkje vis nye framhevingar for tut som nyleg har vorte heva fram (Påverkar berre nylege framhevingar) setting_always_send_emails: Vanlegvis vil ikkje e-postvarsel bli sendt når du brukar Mastodon aktivt - setting_default_sensitive: Nærtakande media vert gøymd som standard og kan synast med eit klikk - setting_display_media_default: Gøym media som er merka som nærtakande + setting_default_sensitive: Sensitive media vert gøymde som standard, og du syner dei ved å klikka på dei + setting_display_media_default: Gøym media som er merka som sensitive setting_display_media_hide_all: Alltid skjul alt media setting_display_media_show_all: Vis alltid media setting_use_blurhash: Overgangar er basert på fargane til skjulte grafikkelement, men gjer detaljar utydelege @@ -218,7 +220,7 @@ nn: setting_theme: Sidetema setting_trends: Vis kva som er populært i dag setting_unfollow_modal: Vis stadfesting før du sluttar å fylgja nokon - setting_use_blurhash: Vis fargerike overgangar for gøymt media + setting_use_blurhash: Vis fargerike overgangar for gøymde medium setting_use_pending_items: Saktemodus severity: Alvorsgrad sign_in_token_attempt: Trygdenykel @@ -233,8 +235,8 @@ nn: name: Emneknagg filters: actions: - hide: Gøym totalt - warn: Gøym med ei advarsel + hide: Gøym heilt + warn: Gøym med ei åtvaring form_admin_settings: activity_api_enabled: Legg ut samla statistikk om brukaraktiviteten i APIet backups_retention_period: Arkiveringsperiode for brukararkiv @@ -264,9 +266,9 @@ nn: trends: Aktiver trendar trends_as_landing_page: Bruk trendar som startside interactions: - must_be_follower: Gøym varslingar frå folk som ikkje fylgjer deg - must_be_following: Gøym varslingar frå folk du ikkje fylgjer - must_be_following_dm: Gøym direktemeldinger frå folk du ikkje fylgjer + must_be_follower: Blokker varsel frå folk som ikkje fylgjer deg + must_be_following: Blokker varsel frå folk du ikkje fylgjer + must_be_following_dm: Blokker direktemeldinger frå folk du ikkje fylgjer invite: comment: Kommentar invite_request: From 036f5a05e372b9b1899907195e48001eec079854 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 27 Feb 2024 15:59:20 +0100 Subject: [PATCH 08/18] Convert the streaming server to ESM (#29389) Co-authored-by: Claire --- streaming/{.eslintrc.js => .eslintrc.cjs} | 12 ++--- streaming/errors.js | 15 ++---- streaming/index.js | 64 +++++++++++++---------- streaming/logging.js | 23 ++++---- streaming/metrics.js | 6 +-- streaming/package.json | 1 + streaming/tsconfig.json | 6 +-- streaming/utils.js | 20 +++---- 8 files changed, 67 insertions(+), 80 deletions(-) rename streaming/{.eslintrc.js => .eslintrc.cjs} (79%) diff --git a/streaming/.eslintrc.js b/streaming/.eslintrc.cjs similarity index 79% rename from streaming/.eslintrc.js rename to streaming/.eslintrc.cjs index 188ebb512d19f1..e25cff7df07c27 100644 --- a/streaming/.eslintrc.js +++ b/streaming/.eslintrc.cjs @@ -1,4 +1,8 @@ +/* eslint-disable import/no-commonjs */ + // @ts-check + +// @ts-ignore - This needs to be a CJS file (eslint does not yet support ESM configs), and TS is complaining we use require const { defineConfig } = require('eslint-define-config'); module.exports = defineConfig({ @@ -22,22 +26,18 @@ module.exports = defineConfig({ // to maintain. 'no-delete-var': 'off', - // The streaming server is written in commonjs, not ESM for now: - 'import/no-commonjs': 'off', - // This overrides the base configuration for this rule to pick up // dependencies for the streaming server from the correct package.json file. 'import/no-extraneous-dependencies': [ 'error', { - devDependencies: [ - 'streaming/.eslintrc.js', - ], + devDependencies: ['streaming/.eslintrc.cjs'], optionalDependencies: false, peerDependencies: false, includeTypes: true, packageDir: __dirname, }, ], + 'import/extensions': ['error', 'always'], }, }); diff --git a/streaming/errors.js b/streaming/errors.js index 9a641180ba4624..6c44d2cb8f33fb 100644 --- a/streaming/errors.js +++ b/streaming/errors.js @@ -5,15 +5,14 @@ * override it in let statements. * @type {string} */ -const UNEXPECTED_ERROR_MESSAGE = 'An unexpected error occurred'; -exports.UNKNOWN_ERROR_MESSAGE = UNEXPECTED_ERROR_MESSAGE; +export const UNEXPECTED_ERROR_MESSAGE = 'An unexpected error occurred'; /** * Extracts the status and message properties from the error object, if * available for public use. The `unknown` is for catch statements * @param {Error | AuthenticationError | RequestError | unknown} err */ -exports.extractStatusAndMessage = function(err) { +export function extractStatusAndMessage(err) { let statusCode = 500; let errorMessage = UNEXPECTED_ERROR_MESSAGE; if (err instanceof AuthenticationError || err instanceof RequestError) { @@ -22,9 +21,9 @@ exports.extractStatusAndMessage = function(err) { } return { statusCode, errorMessage }; -}; +} -class RequestError extends Error { +export class RequestError extends Error { /** * @param {string} message */ @@ -35,9 +34,7 @@ class RequestError extends Error { } } -exports.RequestError = RequestError; - -class AuthenticationError extends Error { +export class AuthenticationError extends Error { /** * @param {string} message */ @@ -47,5 +44,3 @@ class AuthenticationError extends Error { this.status = 401; } } - -exports.AuthenticationError = AuthenticationError; diff --git a/streaming/index.js b/streaming/index.js index 1c312ebd70b21b..fa30260a3a4260 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -1,32 +1,36 @@ // @ts-check -const fs = require('fs'); -const http = require('http'); -const path = require('path'); -const url = require('url'); - -const cors = require('cors'); -const dotenv = require('dotenv'); -const express = require('express'); -const { Redis } = require('ioredis'); -const { JSDOM } = require('jsdom'); -const pg = require('pg'); -const dbUrlToConfig = require('pg-connection-string').parse; -const WebSocket = require('ws'); - -const errors = require('./errors'); -const { AuthenticationError, RequestError } = require('./errors'); -const { logger, httpLogger, initializeLogLevel, attachWebsocketHttpLogger, createWebsocketLogger } = require('./logging'); -const { setupMetrics } = require('./metrics'); -const { isTruthy, normalizeHashtag, firstParam } = require("./utils"); +import fs from 'node:fs'; +import http from 'node:http'; +import path from 'node:path'; +import url from 'node:url'; + +import cors from 'cors'; +import dotenv from 'dotenv'; +import express from 'express'; +import { Redis } from 'ioredis'; +import { JSDOM } from 'jsdom'; +import pg from 'pg'; +import pgConnectionString from 'pg-connection-string'; +import WebSocket from 'ws'; + +import { AuthenticationError, RequestError, extractStatusAndMessage as extractErrorStatusAndMessage } from './errors.js'; +import { logger, httpLogger, initializeLogLevel, attachWebsocketHttpLogger, createWebsocketLogger } from './logging.js'; +import { setupMetrics } from './metrics.js'; +import { isTruthy, normalizeHashtag, firstParam } from './utils.js'; const environment = process.env.NODE_ENV || 'development'; // Correctly detect and load .env or .env.production file based on environment: const dotenvFile = environment === 'production' ? '.env.production' : '.env'; +const dotenvFilePath = path.resolve( + url.fileURLToPath( + new URL(path.join('..', dotenvFile), import.meta.url) + ) +); dotenv.config({ - path: path.resolve(__dirname, path.join('..', dotenvFile)) + path: dotenvFilePath }); initializeLogLevel(process.env, environment); @@ -143,7 +147,7 @@ const pgConfigFromEnv = (env) => { let baseConfig = {}; if (env.DATABASE_URL) { - const parsedUrl = dbUrlToConfig(env.DATABASE_URL); + const parsedUrl = pgConnectionString.parse(env.DATABASE_URL); // The result of dbUrlToConfig from pg-connection-string is not type // compatible with pg.PoolConfig, since parts of the connection URL may be @@ -326,7 +330,7 @@ const startServer = async () => { // Unfortunately for using the on('upgrade') setup, we need to manually // write a HTTP Response to the Socket to close the connection upgrade // attempt, so the following code is to handle all of that. - const {statusCode, errorMessage } = errors.extractStatusAndMessage(err); + const {statusCode, errorMessage } = extractErrorStatusAndMessage(err); /** @type {Record} */ const headers = { @@ -748,7 +752,7 @@ const startServer = async () => { return; } - const {statusCode, errorMessage } = errors.extractStatusAndMessage(err); + const {statusCode, errorMessage } = extractErrorStatusAndMessage(err); res.writeHead(statusCode, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: errorMessage })); @@ -1155,7 +1159,7 @@ const startServer = async () => { // @ts-ignore streamFrom(channelIds, req, req.log, onSend, onEnd, 'eventsource', options.needsFiltering); }).catch(err => { - const {statusCode, errorMessage } = errors.extractStatusAndMessage(err); + const {statusCode, errorMessage } = extractErrorStatusAndMessage(err); res.log.info({ err }, 'Eventsource subscription error'); @@ -1353,7 +1357,7 @@ const startServer = async () => { stopHeartbeat, }; }).catch(err => { - const {statusCode, errorMessage } = errors.extractStatusAndMessage(err); + const {statusCode, errorMessage } = extractErrorStatusAndMessage(err); logger.error({ err }, 'Websocket subscription error'); @@ -1482,13 +1486,15 @@ const startServer = async () => { // Decrement the metrics for connected clients: connectedClients.labels({ type: 'websocket' }).dec(); - // We need to delete the session object as to ensure it correctly gets + // We need to unassign the session object as to ensure it correctly gets // garbage collected, without doing this we could accidentally hold on to // references to the websocket, the request, and the logger, causing // memory leaks. - // - // @ts-ignore - delete session; + + // This is commented out because `delete` only operated on object properties + // It needs to be replaced by `session = undefined`, but it requires every calls to + // `session` to check for it, thus a significant refactor + // delete session; }); // Note: immediately after the `error` event is emitted, the `close` event diff --git a/streaming/logging.js b/streaming/logging.js index 64ee474875ddde..e1c552c22ed8f1 100644 --- a/streaming/logging.js +++ b/streaming/logging.js @@ -1,6 +1,6 @@ -const { pino } = require('pino'); -const { pinoHttp, stdSerializers: pinoHttpSerializers } = require('pino-http'); -const uuid = require('uuid'); +import { pino } from 'pino'; +import { pinoHttp, stdSerializers as pinoHttpSerializers } from 'pino-http'; +import * as uuid from 'uuid'; /** * Generates the Request ID for logging and setting on responses @@ -36,7 +36,7 @@ function sanitizeRequestLog(req) { return log; } -const logger = pino({ +export const logger = pino({ name: "streaming", // Reformat the log level to a string: formatters: { @@ -59,7 +59,7 @@ const logger = pino({ } }); -const httpLogger = pinoHttp({ +export const httpLogger = pinoHttp({ logger, genReqId: generateRequestId, serializers: { @@ -71,7 +71,7 @@ const httpLogger = pinoHttp({ * Attaches a logger to the request object received by http upgrade handlers * @param {http.IncomingMessage} request */ -function attachWebsocketHttpLogger(request) { +export function attachWebsocketHttpLogger(request) { generateRequestId(request); request.log = logger.child({ @@ -84,7 +84,7 @@ function attachWebsocketHttpLogger(request) { * @param {http.IncomingMessage} request * @param {import('./index.js').ResolvedAccount} resolvedAccount */ -function createWebsocketLogger(request, resolvedAccount) { +export function createWebsocketLogger(request, resolvedAccount) { // ensure the request.id is always present. generateRequestId(request); @@ -98,17 +98,12 @@ function createWebsocketLogger(request, resolvedAccount) { }); } -exports.logger = logger; -exports.httpLogger = httpLogger; -exports.attachWebsocketHttpLogger = attachWebsocketHttpLogger; -exports.createWebsocketLogger = createWebsocketLogger; - /** * Initializes the log level based on the environment * @param {Object} env * @param {string} environment */ -exports.initializeLogLevel = function initializeLogLevel(env, environment) { +export function initializeLogLevel(env, environment) { if (env.LOG_LEVEL && Object.keys(logger.levels.values).includes(env.LOG_LEVEL)) { logger.level = env.LOG_LEVEL; } else if (environment === 'development') { @@ -116,4 +111,4 @@ exports.initializeLogLevel = function initializeLogLevel(env, environment) { } else { logger.level = 'info'; } -}; +} diff --git a/streaming/metrics.js b/streaming/metrics.js index d05b4c9b16affd..a029d778fcf67a 100644 --- a/streaming/metrics.js +++ b/streaming/metrics.js @@ -1,6 +1,6 @@ // @ts-check -const metrics = require('prom-client'); +import metrics from 'prom-client'; /** * @typedef StreamingMetrics @@ -18,7 +18,7 @@ const metrics = require('prom-client'); * @param {import('pg').Pool} pgPool * @returns {StreamingMetrics} */ -function setupMetrics(channels, pgPool) { +export function setupMetrics(channels, pgPool) { // Collect metrics from Node.js metrics.collectDefaultMetrics(); @@ -101,5 +101,3 @@ function setupMetrics(channels, pgPool) { messagesSent, }; } - -exports.setupMetrics = setupMetrics; diff --git a/streaming/package.json b/streaming/package.json index 71f204c0fbfb17..efb692578cb487 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -7,6 +7,7 @@ }, "description": "Mastodon's Streaming Server", "private": true, + "type": "module", "repository": { "type": "git", "url": "https://github.com/mastodon/mastodon.git" diff --git a/streaming/tsconfig.json b/streaming/tsconfig.json index a0cf68ef90942f..ba5bd51ff7509d 100644 --- a/streaming/tsconfig.json +++ b/streaming/tsconfig.json @@ -2,11 +2,11 @@ "extends": "../tsconfig.json", "compilerOptions": { "target": "esnext", - "module": "CommonJS", - "moduleResolution": "node", + "module": "NodeNext", + "moduleResolution": "NodeNext", "noUnusedParameters": false, "tsBuildInfoFile": "../tmp/cache/streaming/tsconfig.tsbuildinfo", "paths": {}, }, - "include": ["./*.js", "./.eslintrc.js"], + "include": ["./*.js", "./.eslintrc.cjs"], } diff --git a/streaming/utils.js b/streaming/utils.js index 7b87a1d14c9fb8..4610bf660d8930 100644 --- a/streaming/utils.js +++ b/streaming/utils.js @@ -16,11 +16,9 @@ const FALSE_VALUES = [ * @param {any} value * @returns {boolean} */ -const isTruthy = value => - value && !FALSE_VALUES.includes(value); - -exports.isTruthy = isTruthy; - +export function isTruthy(value) { + return value && !FALSE_VALUES.includes(value); +} /** * See app/lib/ascii_folder.rb for the canon definitions @@ -33,7 +31,7 @@ const EQUIVALENT_ASCII_CHARS = 'AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEe * @param {string} str * @returns {string} */ -function foldToASCII(str) { +export function foldToASCII(str) { const regex = new RegExp(NON_ASCII_CHARS.split('').join('|'), 'g'); return str.replace(regex, function(match) { @@ -42,28 +40,22 @@ function foldToASCII(str) { }); } -exports.foldToASCII = foldToASCII; - /** * @param {string} str * @returns {string} */ -function normalizeHashtag(str) { +export function normalizeHashtag(str) { return foldToASCII(str.normalize('NFKC').toLowerCase()).replace(/[^\p{L}\p{N}_\u00b7\u200c]/gu, ''); } -exports.normalizeHashtag = normalizeHashtag; - /** * @param {string|string[]} arrayOrString * @returns {string} */ -function firstParam(arrayOrString) { +export function firstParam(arrayOrString) { if (Array.isArray(arrayOrString)) { return arrayOrString[0]; } else { return arrayOrString; } } - -exports.firstParam = firstParam; From 6f7615ba86afda56e1d661442286a1d68467a525 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 27 Feb 2024 16:18:06 +0100 Subject: [PATCH 09/18] Add basic end-to-end test for admin moderation interface (#29424) --- spec/support/stories/profile_stories.rb | 6 +++++ spec/support/streaming_server_manager.rb | 3 +++ spec/system/report_interface_spec.rb | 31 ++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 spec/system/report_interface_spec.rb diff --git a/spec/support/stories/profile_stories.rb b/spec/support/stories/profile_stories.rb index 74342c337d6688..f5fc9a441fd698 100644 --- a/spec/support/stories/profile_stories.rb +++ b/spec/support/stories/profile_stories.rb @@ -21,6 +21,12 @@ def as_a_logged_in_user click_on I18n.t('auth.login') end + def as_a_logged_in_admin + # This is a bit awkward, but this avoids code duplication. + as_a_logged_in_user + bob.update!(role: UserRole.find_by!(name: 'Admin')) + end + def with_alice_as_local_user @alice_bio = '@alice and @bob are fictional characters commonly used as' \ 'placeholder names in #cryptology, as well as #science and' \ diff --git a/spec/support/streaming_server_manager.rb b/spec/support/streaming_server_manager.rb index 33819182990940..b702fc77cef597 100644 --- a/spec/support/streaming_server_manager.rb +++ b/spec/support/streaming_server_manager.rb @@ -109,6 +109,9 @@ def stop # Also needs to be set per-example here because of the database cleaner. Setting.registrations_mode = 'open' + # Load seeds so we have the default roles otherwise cleared by `DatabaseCleaner` + Rails.application.load_seed + example.run end diff --git a/spec/system/report_interface_spec.rb b/spec/system/report_interface_spec.rb new file mode 100644 index 00000000000000..6eba5525594f39 --- /dev/null +++ b/spec/system/report_interface_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'report interface', :paperclip_processing do + include ProfileStories + + let(:email) { 'admin@example.com' } + let(:password) { 'password' } + let(:confirmed_at) { Time.zone.now } + let(:finished_onboarding) { true } + + let(:reported_account) { Fabricate(:account) } + let(:reported_status) { Fabricate(:status, account: reported_account) } + let(:media_attachment) { Fabricate(:media_attachment, account: reported_account, status: reported_status, file: attachment_fixture('attachment.jpg')) } + let!(:report) { Fabricate(:report, target_account: reported_account, status_ids: [media_attachment.status.id]) } + + before do + as_a_logged_in_admin + visit admin_report_path(report) + end + + it 'displays the report interface, including the javascript bits' do + # The report category selector React component is properly rendered + expect(page).to have_css('.report-reason-selector') + + # The media React component is properly rendered + page.scroll_to(page.find('.batch-table__row')) + expect(page).to have_css('.spoiler-button__overlay__label') + end +end From 899eac1a92c802c334606a49d538ef37bd47c91a Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 27 Feb 2024 16:42:05 +0100 Subject: [PATCH 10/18] Use modern ES syntax rather than `.call` (#29368) --- .../mastodon/containers/media_container.jsx | 2 +- .../features/emoji/emoji_compressed.js | 4 ++-- .../mastodon/features/emoji/emoji_utils.js | 10 ++++---- app/javascript/packs/admin.jsx | 24 +++++++++++-------- app/javascript/packs/public.jsx | 14 +++++------ public/embed.js | 4 +++- streaming/index.js | 4 ++-- 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/app/javascript/mastodon/containers/media_container.jsx b/app/javascript/mastodon/containers/media_container.jsx index fba3c5df78bef6..d18602e3b5a46a 100644 --- a/app/javascript/mastodon/containers/media_container.jsx +++ b/app/javascript/mastodon/containers/media_container.jsx @@ -80,7 +80,7 @@ export default class MediaContainer extends PureComponent { return ( <> - {[].map.call(components, (component, i) => { + {Array.from(components).map((component, i) => { const componentName = component.getAttribute('data-component'); const Component = MEDIA_COMPONENTS[componentName]; const { media, card, poll, hashtag, ...props } = JSON.parse(component.getAttribute('data-props')); diff --git a/app/javascript/mastodon/features/emoji/emoji_compressed.js b/app/javascript/mastodon/features/emoji/emoji_compressed.js index a4863566dad3ac..ed8e9bbe303f64 100644 --- a/app/javascript/mastodon/features/emoji/emoji_compressed.js +++ b/app/javascript/mastodon/features/emoji/emoji_compressed.js @@ -36,7 +36,7 @@ Object.keys(emojiIndex.emojis).forEach(key => { let emoji = emojiIndex.emojis[key]; // Emojis with skin tone modifiers are stored like this - if (Object.prototype.hasOwnProperty.call(emoji, '1')) { + if (Object.hasOwn(emoji, '1')) { emoji = emoji['1']; } @@ -88,7 +88,7 @@ Object.keys(emojiIndex.emojis).forEach(key => { let emoji = emojiIndex.emojis[key]; // Emojis with skin tone modifiers are stored like this - if (Object.prototype.hasOwnProperty.call(emoji, '1')) { + if (Object.hasOwn(emoji, '1')) { emoji = emoji['1']; } diff --git a/app/javascript/mastodon/features/emoji/emoji_utils.js b/app/javascript/mastodon/features/emoji/emoji_utils.js index 83bcc9d82f9f14..c13d25056706a4 100644 --- a/app/javascript/mastodon/features/emoji/emoji_utils.js +++ b/app/javascript/mastodon/features/emoji/emoji_utils.js @@ -135,19 +135,19 @@ function getData(emoji, skin, set) { } } - if (Object.prototype.hasOwnProperty.call(data.short_names, emoji)) { + if (Object.hasOwn(data.short_names, emoji)) { emoji = data.short_names[emoji]; } - if (Object.prototype.hasOwnProperty.call(data.emojis, emoji)) { + if (Object.hasOwn(data.emojis, emoji)) { emojiData = data.emojis[emoji]; } } else if (emoji.id) { - if (Object.prototype.hasOwnProperty.call(data.short_names, emoji.id)) { + if (Object.hasOwn(data.short_names, emoji.id)) { emoji.id = data.short_names[emoji.id]; } - if (Object.prototype.hasOwnProperty.call(data.emojis, emoji.id)) { + if (Object.hasOwn(data.emojis, emoji.id)) { emojiData = data.emojis[emoji.id]; skin = skin || emoji.skin; } @@ -216,7 +216,7 @@ function deepMerge(a, b) { let originalValue = a[key], value = originalValue; - if (Object.prototype.hasOwnProperty.call(b, key)) { + if (Object.hasOwn(b, key)) { value = b[key]; } diff --git a/app/javascript/packs/admin.jsx b/app/javascript/packs/admin.jsx index 343678c184e163..5f24d6677e949c 100644 --- a/app/javascript/packs/admin.jsx +++ b/app/javascript/packs/admin.jsx @@ -46,7 +46,7 @@ const hideSelectAll = () => { Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => { const selectAllMatchingElement = document.querySelector('.batch-table__select-all'); - [].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => { + document.querySelectorAll(batchCheckboxClassName).forEach((content) => { content.checked = target.checked; }); @@ -81,8 +81,11 @@ Rails.delegate(document, batchCheckboxClassName, 'change', () => { const selectAllMatchingElement = document.querySelector('.batch-table__select-all'); if (checkAllElement) { - checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); - checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); + const allCheckboxes = Array.from( + document.querySelectorAll(batchCheckboxClassName) + ); + checkAllElement.checked = allCheckboxes.every((content) => content.checked); + checkAllElement.indeterminate = !checkAllElement.checked && allCheckboxes.some((content) => content.checked); if (selectAllMatchingElement) { if (checkAllElement.checked) { @@ -133,11 +136,11 @@ Rails.delegate(document, '#form_admin_settings_enable_bootstrap_timeline_account const onChangeRegistrationMode = (target) => { const enabled = target.value === 'approved'; - [].forEach.call(document.querySelectorAll('.form_admin_settings_registrations_mode .warning-hint'), (warning_hint) => { + document.querySelectorAll('.form_admin_settings_registrations_mode .warning-hint').forEach((warning_hint) => { warning_hint.style.display = target.value === 'open' ? 'inline' : 'none'; }); - [].forEach.call(document.querySelectorAll('#form_admin_settings_require_invite_text'), (input) => { + document.querySelectorAll('#form_admin_settings_require_invite_text').forEach((input) => { input.disabled = !enabled; if (enabled) { let element = input; @@ -183,8 +186,9 @@ ready(() => { const checkAllElement = document.querySelector('#batch_checkbox_all'); if (checkAllElement) { - checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); - checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); + const allCheckboxes = Array.from(document.querySelectorAll(batchCheckboxClassName)); + checkAllElement.checked = allCheckboxes.every( (content) => content.checked); + checkAllElement.indeterminate = !checkAllElement.checked && allCheckboxes.some((content) => content.checked); } document.querySelector('a#add-instance-button')?.addEventListener('click', (e) => { @@ -197,7 +201,7 @@ ready(() => { } }); - [].forEach.call(document.querySelectorAll('input[type="datetime-local"]'), element => { + document.querySelectorAll('input[type="datetime-local"]').forEach(element => { if (element.value) { element.value = convertUTCDateTimeToLocal(element.value); } @@ -207,7 +211,7 @@ ready(() => { }); Rails.delegate(document, 'form', 'submit', ({ target }) => { - [].forEach.call(target.querySelectorAll('input[type="datetime-local"]'), element => { + target.querySelectorAll('input[type="datetime-local"]').forEach(element => { if (element.value && element.validity.valid) { element.value = convertLocalDatetimeToUTC(element.value); } @@ -219,7 +223,7 @@ ready(() => { setAnnouncementEndsAttributes(announcementStartsAt); } - [].forEach.call(document.querySelectorAll('[data-admin-component]'), element => { + document.querySelectorAll('[data-admin-component]').forEach(element => { const componentName = element.getAttribute('data-admin-component'); const componentProps = JSON.parse(element.getAttribute('data-props')); diff --git a/app/javascript/packs/public.jsx b/app/javascript/packs/public.jsx index 5edc3553700406..01b57dce398408 100644 --- a/app/javascript/packs/public.jsx +++ b/app/javascript/packs/public.jsx @@ -73,11 +73,11 @@ function loaded() { return messageFormat.format(values); }; - [].forEach.call(document.querySelectorAll('.emojify'), (content) => { + document.querySelectorAll('.emojify').forEach((content) => { content.innerHTML = emojify(content.innerHTML); }); - [].forEach.call(document.querySelectorAll('time.formatted'), (content) => { + document.querySelectorAll('time.formatted').forEach((content) => { const datetime = new Date(content.getAttribute('datetime')); const formattedDate = dateTimeFormat.format(datetime); @@ -94,7 +94,7 @@ function loaded() { }; const todayFormat = new IntlMessageFormat(localeData['relative_format.today'] || 'Today at {time}', locale); - [].forEach.call(document.querySelectorAll('time.relative-formatted'), (content) => { + document.querySelectorAll('time.relative-formatted').forEach((content) => { const datetime = new Date(content.getAttribute('datetime')); let formattedContent; @@ -111,7 +111,7 @@ function loaded() { content.textContent = formattedContent; }); - [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => { + document.querySelectorAll('time.time-ago').forEach((content) => { const datetime = new Date(content.getAttribute('datetime')); const now = new Date(); @@ -128,8 +128,8 @@ function loaded() { if (reactComponents.length > 0) { import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container') .then(({ default: MediaContainer }) => { - [].forEach.call(reactComponents, (component) => { - [].forEach.call(component.children, (child) => { + reactComponents.forEach((component) => { + Array.from(component.children).forEach((child) => { component.removeChild(child); }); }); @@ -185,7 +185,7 @@ function loaded() { return false; }); - [].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => { + document.querySelectorAll('.status__content__spoiler-link').forEach((spoilerLink) => { const statusEl = spoilerLink.parentNode.parentNode; const message = (statusEl.dataset.spoiler === 'expanded') ? (localeData['status.show_less'] || 'Show less') : (localeData['status.show_more'] || 'Show more'); spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format(); diff --git a/public/embed.js b/public/embed.js index defba403e47d36..f8e6a22db44114 100644 --- a/public/embed.js +++ b/public/embed.js @@ -31,6 +31,8 @@ var iframe = iframes.get(data.id); + if(!iframe) return; + if ('source' in e && iframe.contentWindow !== e.source) { return; } @@ -38,7 +40,7 @@ iframe.height = data.height; }); - [].forEach.call(document.querySelectorAll('iframe.mastodon-embed'), function (iframe) { + document.querySelectorAll('iframe.mastodon-embed').forEach(iframe => { // select unique id for each iframe var id = 0, failCount = 0, idBuffer = new Uint32Array(1); while (id === 0 || iframes.has(id)) { diff --git a/streaming/index.js b/streaming/index.js index fa30260a3a4260..154ecbc02c5f5a 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -192,7 +192,7 @@ const pgConfigFromEnv = (env) => { if (!baseConfig.password && env.DB_PASS) { baseConfig.password = env.DB_PASS; } - } else if (Object.hasOwnProperty.call(pgConfigs, environment)) { + } else if (Object.hasOwn(pgConfigs, environment)) { baseConfig = pgConfigs[environment]; if (env.DB_SSLMODE) { @@ -912,7 +912,7 @@ const startServer = async () => { // If the payload already contains the `filtered` property, it means // that filtering has been applied on the ruby on rails side, as // such, we don't need to construct or apply the filters in streaming: - if (Object.prototype.hasOwnProperty.call(payload, "filtered")) { + if (Object.hasOwn(payload, "filtered")) { transmit(event, payload); return; } From dc4427dc9ba4d084d96153feb2eef6a32c288e1f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:07:43 +0100 Subject: [PATCH 11/18] Update devDependencies (non-major) (#29089) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Renaud Chaput --- .devcontainer/codespaces/devcontainer.json | 18 +++++----- .devcontainer/devcontainer.json | 16 ++++----- app/javascript/mastodon/test_helpers.tsx | 2 +- jsconfig.json | 4 +-- streaming/tsconfig.json | 4 +-- tsconfig.json | 8 ++--- yarn.lock | 40 +++++++++++----------- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/.devcontainer/codespaces/devcontainer.json b/.devcontainer/codespaces/devcontainer.json index b32e4026d2374d..ca9156fdaa4bf1 100644 --- a/.devcontainer/codespaces/devcontainer.json +++ b/.devcontainer/codespaces/devcontainer.json @@ -5,7 +5,7 @@ "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "features": { - "ghcr.io/devcontainers/features/sshd:1": {}, + "ghcr.io/devcontainers/features/sshd:1": {} }, "runServices": ["app", "db", "redis"], @@ -15,16 +15,16 @@ "portsAttributes": { "3000": { "label": "web", - "onAutoForward": "notify", + "onAutoForward": "notify" }, "4000": { "label": "stream", - "onAutoForward": "silent", - }, + "onAutoForward": "silent" + } }, "otherPortsAttributes": { - "onAutoForward": "silent", + "onAutoForward": "silent" }, "remoteEnv": { @@ -33,7 +33,7 @@ "STREAMING_API_BASE_URL": "https://${localEnv:CODESPACE_NAME}-4000.app.github.dev", "DISABLE_FORGERY_REQUEST_PROTECTION": "true", "ES_ENABLED": "", - "LIBRE_TRANSLATE_ENDPOINT": "", + "LIBRE_TRANSLATE_ENDPOINT": "" }, "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", @@ -43,7 +43,7 @@ "customizations": { "vscode": { "settings": {}, - "extensions": ["EditorConfig.EditorConfig", "webben.browserslist"], - }, - }, + "extensions": ["EditorConfig.EditorConfig", "webben.browserslist"] + } + } } diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ed71235b3b3001..fa8d6542c18aec 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,7 @@ "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "features": { - "ghcr.io/devcontainers/features/sshd:1": {}, + "ghcr.io/devcontainers/features/sshd:1": {} }, "forwardPorts": [3000, 4000], @@ -14,17 +14,17 @@ "3000": { "label": "web", "onAutoForward": "notify", - "requireLocalPort": true, + "requireLocalPort": true }, "4000": { "label": "stream", "onAutoForward": "silent", - "requireLocalPort": true, - }, + "requireLocalPort": true + } }, "otherPortsAttributes": { - "onAutoForward": "silent", + "onAutoForward": "silent" }, "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", @@ -34,7 +34,7 @@ "customizations": { "vscode": { "settings": {}, - "extensions": ["EditorConfig.EditorConfig", "webben.browserslist"], - }, - }, + "extensions": ["EditorConfig.EditorConfig", "webben.browserslist"] + } + } } diff --git a/app/javascript/mastodon/test_helpers.tsx b/app/javascript/mastodon/test_helpers.tsx index 689589556956b4..69d57b95a0437b 100644 --- a/app/javascript/mastodon/test_helpers.tsx +++ b/app/javascript/mastodon/test_helpers.tsx @@ -40,7 +40,7 @@ function render( ui: React.ReactElement, { locale = 'en', signedIn = true, ...renderOptions } = {}, ) { - const Wrapper = (props: { children: React.ReactElement }) => { + const Wrapper = (props: { children: React.ReactNode }) => { return ( diff --git a/jsconfig.json b/jsconfig.json index 7b710de83c4fda..d52816a98b68ef 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -11,7 +11,7 @@ "noEmit": true, "resolveJsonModule": true, "strict": false, - "target": "ES2022", + "target": "ES2022" }, - "exclude": ["**/build/*", "**/node_modules/*", "**/public/*", "**/vendor/*"], + "exclude": ["**/build/*", "**/node_modules/*", "**/public/*", "**/vendor/*"] } diff --git a/streaming/tsconfig.json b/streaming/tsconfig.json index ba5bd51ff7509d..37e9a7fee026e3 100644 --- a/streaming/tsconfig.json +++ b/streaming/tsconfig.json @@ -6,7 +6,7 @@ "moduleResolution": "NodeNext", "noUnusedParameters": false, "tsBuildInfoFile": "../tmp/cache/streaming/tsconfig.tsbuildinfo", - "paths": {}, + "paths": {} }, - "include": ["./*.js", "./.eslintrc.cjs"], + "include": ["./*.js", "./.eslintrc.cjs"] } diff --git a/tsconfig.json b/tsconfig.json index dc71fc4a9cad8e..a193ea35f2fdac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,12 +15,12 @@ "paths": { "mastodon": ["app/javascript/mastodon"], "mastodon/*": ["app/javascript/mastodon/*"], - "@/*": ["app/javascript/*"], - }, + "@/*": ["app/javascript/*"] + } }, "include": [ "app/javascript/mastodon", "app/javascript/packs", - "app/javascript/types", - ], + "app/javascript/types" + ] } diff --git a/yarn.lock b/yarn.lock index 943c488714735e..442473a0922298 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2881,8 +2881,8 @@ __metadata: linkType: hard "@testing-library/jest-dom@npm:^6.0.0": - version: 6.4.0 - resolution: "@testing-library/jest-dom@npm:6.4.0" + version: 6.4.2 + resolution: "@testing-library/jest-dom@npm:6.4.2" dependencies: "@adobe/css-tools": "npm:^4.3.2" "@babel/runtime": "npm:^7.9.2" @@ -2909,13 +2909,13 @@ __metadata: optional: true vitest: optional: true - checksum: 10c0/6b7eba9ca388986a721fb12f84adf0f5534bf7ec5851982023a889c4a0afac6e9e91291bdac39e1f59a05adefd7727e30463d98b21c3da32fbfec229ccb11ef1 + checksum: 10c0/e7eba527b34ce30cde94424d2ec685bdfed51daaafb7df9b68b51aec6052e99a50c8bfe654612dacdf857a1eb81d68cf294fc89de558ee3a992bf7a6019fffcc languageName: node linkType: hard "@testing-library/react@npm:^14.0.0": - version: 14.1.2 - resolution: "@testing-library/react@npm:14.1.2" + version: 14.2.1 + resolution: "@testing-library/react@npm:14.2.1" dependencies: "@babel/runtime": "npm:^7.12.5" "@testing-library/dom": "npm:^9.0.0" @@ -2923,7 +2923,7 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 10c0/b5b0990d3aa0ea8b37c55804e0d5d584fc638a5c7d4df90da9a0fdb00bc981b27b6991468b2dc719982a5d0b0107a41596063ce51ad519eeab47b22bc04d6779 + checksum: 10c0/83b35cf8bf5640f1b63b32223ebc75799dc1a8e034d819120b26838fba0b0ab10bdbe6ad07dd8ae8287365f2b0c52dc9892a6fa11bb24d3e63ad97dfb7f2f296 languageName: node linkType: hard @@ -10903,28 +10903,28 @@ __metadata: linkType: hard "lint-staged@npm:^15.0.0": - version: 15.2.0 - resolution: "lint-staged@npm:15.2.0" + version: 15.2.2 + resolution: "lint-staged@npm:15.2.2" dependencies: chalk: "npm:5.3.0" commander: "npm:11.1.0" debug: "npm:4.3.4" execa: "npm:8.0.1" lilconfig: "npm:3.0.0" - listr2: "npm:8.0.0" + listr2: "npm:8.0.1" micromatch: "npm:4.0.5" pidtree: "npm:0.6.0" string-argv: "npm:0.3.2" yaml: "npm:2.3.4" bin: lint-staged: bin/lint-staged.js - checksum: 10c0/4a1ff25dd06dbd4346fd244c9a0ebb936532ba18c0caedeb895c2e232f3c6c5fd08f6667624716660bc29e3e0f9f0440a9175114394616e991ebd5fab4b1f092 + checksum: 10c0/a1ba6c7ee53e30a0f6ea9a351d95d3d0d2be916a41b561e22907e9ea513eb18cb3dbe65bff3ec13fad15777999efe56b2e2a95427e31d12a9b7e7948c3630ee2 languageName: node linkType: hard -"listr2@npm:8.0.0": - version: 8.0.0 - resolution: "listr2@npm:8.0.0" +"listr2@npm:8.0.1": + version: 8.0.1 + resolution: "listr2@npm:8.0.1" dependencies: cli-truncate: "npm:^4.0.0" colorette: "npm:^2.0.20" @@ -10932,7 +10932,7 @@ __metadata: log-update: "npm:^6.0.0" rfdc: "npm:^1.3.0" wrap-ansi: "npm:^9.0.0" - checksum: 10c0/6e356df9127c68b69186c927c993645223557e941a76b0bb210e35786aedc53f577df437251db804606ff37ac509c5d945289a84b3daee7fadf2e3dcb889ecc9 + checksum: 10c0/b565d6ceb3a4c2dbe0c1735c0fd907afd0d6f89de21aced8e05187b2d88ca2f8f9ebc5d743885396a00f05f13146f6be744d098a56ce0402cf1cd131485a7ff1 languageName: node linkType: hard @@ -13295,11 +13295,11 @@ __metadata: linkType: hard "prettier@npm:^3.0.0": - version: 3.2.4 - resolution: "prettier@npm:3.2.4" + version: 3.2.5 + resolution: "prettier@npm:3.2.5" bin: prettier: bin/prettier.cjs - checksum: 10c0/88dfeb78ac6096522c9a5b81f1413d875f568420d9bb6a5e5103527912519b993f2bcdcac311fcff5718d5869671d44e4f85827d3626f3a6ce32b9abc65d88e0 + checksum: 10c0/ea327f37a7d46f2324a34ad35292af2ad4c4c3c3355da07313339d7e554320f66f65f91e856add8530157a733c6c4a897dc41b577056be5c24c40f739f5ee8c6 languageName: node linkType: hard @@ -15785,8 +15785,8 @@ __metadata: linkType: hard "stylelint@npm:^16.0.2": - version: 16.2.0 - resolution: "stylelint@npm:16.2.0" + version: 16.2.1 + resolution: "stylelint@npm:16.2.1" dependencies: "@csstools/css-parser-algorithms": "npm:^2.5.0" "@csstools/css-tokenizer": "npm:^2.2.3" @@ -15828,7 +15828,7 @@ __metadata: write-file-atomic: "npm:^5.0.1" bin: stylelint: bin/stylelint.mjs - checksum: 10c0/6fdf0451833c11b18c9aa502f687febd6881a912ac94f39d509b894b0f74ccb636f3dac2991c69cc82dc6190731cc2fa48e307fed477d2a0fce57067cd22b572 + checksum: 10c0/eeaba06885e542c832e5cffc07b2d0dabdc5a72e6ad4d6cb3d01dcc260c29a712b0b935cbd40e059abd68a100e0563fbc617fc4c9bef3b14ecaf6eea651d9d9d languageName: node linkType: hard From 3b3144740805c1b404934c0f196d6cddc14dac0b Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 27 Feb 2024 17:09:27 +0100 Subject: [PATCH 12/18] Rework Prettier invocation (#28851) --- .eslintrc.js | 4 +- .github/workflows/format-check.yml | 18 ++ .github/workflows/lint-css.yml | 2 +- .github/workflows/lint-json.yml | 38 ---- .github/workflows/lint-md.yml | 38 ---- .github/workflows/lint-yml.yml | 40 ----- .prettierignore | 8 + package.json | 20 +-- yarn.lock | 267 ++++------------------------- 9 files changed, 74 insertions(+), 361 deletions(-) create mode 100644 .github/workflows/format-check.yml delete mode 100644 .github/workflows/lint-json.yml delete mode 100644 .github/workflows/lint-md.yml delete mode 100644 .github/workflows/lint-yml.yml diff --git a/.eslintrc.js b/.eslintrc.js index ebe07f6e79a25e..bd818c3ce4b171 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -338,7 +338,6 @@ module.exports = defineConfig({ 'plugin:import/typescript', 'plugin:promise/recommended', 'plugin:jsdoc/recommended-typescript', - 'plugin:prettier/recommended', ], parserOptions: { @@ -347,6 +346,9 @@ module.exports = defineConfig({ }, rules: { + // Disable formatting rules that have been enabled in the base config + 'indent': 'off', + 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml new file mode 100644 index 00000000000000..2d483b50229626 --- /dev/null +++ b/.github/workflows/format-check.yml @@ -0,0 +1,18 @@ +name: Check formatting +on: + push: + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@v4 + + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript + + - name: Check formatting with Prettier + run: yarn format:check diff --git a/.github/workflows/lint-css.yml b/.github/workflows/lint-css.yml index 7229bec582210b..e5f487487719f1 100644 --- a/.github/workflows/lint-css.yml +++ b/.github/workflows/lint-css.yml @@ -43,4 +43,4 @@ jobs: - run: echo "::add-matcher::.github/stylelint-matcher.json" - name: Stylelint - run: yarn lint:sass + run: yarn lint:css diff --git a/.github/workflows/lint-json.yml b/.github/workflows/lint-json.yml deleted file mode 100644 index 7796bf92c4aeb1..00000000000000 --- a/.github/workflows/lint-json.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: JSON Linting -on: - push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' - paths: - - 'package.json' - - 'yarn.lock' - - '.nvmrc' - - '.prettier*' - - '**/*.json' - - '.github/workflows/lint-json.yml' - - '!app/javascript/mastodon/locales/*.json' - - pull_request: - paths: - - 'package.json' - - 'yarn.lock' - - '.nvmrc' - - '.prettier*' - - '**/*.json' - - '.github/workflows/lint-json.yml' - - '!app/javascript/mastodon/locales/*.json' - -jobs: - lint: - runs-on: ubuntu-latest - - steps: - - name: Clone repository - uses: actions/checkout@v4 - - - name: Set up Javascript environment - uses: ./.github/actions/setup-javascript - - - name: Prettier - run: yarn lint:json diff --git a/.github/workflows/lint-md.yml b/.github/workflows/lint-md.yml deleted file mode 100644 index 51c59937a30c44..00000000000000 --- a/.github/workflows/lint-md.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Markdown Linting -on: - push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' - paths: - - '.github/workflows/lint-md.yml' - - '.nvmrc' - - '.prettier*' - - '**/*.md' - - '!AUTHORS.md' - - 'package.json' - - 'yarn.lock' - - pull_request: - paths: - - '.github/workflows/lint-md.yml' - - '.nvmrc' - - '.prettier*' - - '**/*.md' - - '!AUTHORS.md' - - 'package.json' - - 'yarn.lock' - -jobs: - lint: - runs-on: ubuntu-latest - - steps: - - name: Clone repository - uses: actions/checkout@v4 - - - name: Set up Javascript environment - uses: ./.github/actions/setup-javascript - - - name: Prettier - run: yarn lint:md diff --git a/.github/workflows/lint-yml.yml b/.github/workflows/lint-yml.yml deleted file mode 100644 index 908bdef5ccfa6c..00000000000000 --- a/.github/workflows/lint-yml.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: YML Linting -on: - push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' - paths: - - 'package.json' - - 'yarn.lock' - - '.nvmrc' - - '.prettier*' - - '**/*.yaml' - - '**/*.yml' - - '.github/workflows/lint-yml.yml' - - '!config/locales/*.yml' - - pull_request: - paths: - - 'package.json' - - 'yarn.lock' - - '.nvmrc' - - '.prettier*' - - '**/*.yaml' - - '**/*.yml' - - '.github/workflows/lint-yml.yml' - - '!config/locales/*.yml' - -jobs: - lint: - runs-on: ubuntu-latest - - steps: - - name: Clone repository - uses: actions/checkout@v4 - - - name: Set up Javascript environment - uses: ./.github/actions/setup-javascript - - - name: Prettier - run: yarn lint:yml diff --git a/.prettierignore b/.prettierignore index 51850b2b28ad74..6b2f0c188945a2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -54,6 +54,13 @@ # Ignore Docker option files docker-compose.override.yml +# Ignore public +/public/assets +/public/emoji +/public/packs +/public/packs-test +/public/system + # Ignore emoji map file /app/javascript/mastodon/features/emoji/emoji_map.json @@ -74,4 +81,5 @@ app/javascript/styles/mastodon/reset.scss # Ignore the generated AUTHORS.md AUTHORS.md +# Process a few selected JS files !lint-staged.config.js diff --git a/package.json b/package.json index af83867fe88c24..4f17b427ee8128 100644 --- a/package.json +++ b/package.json @@ -12,20 +12,16 @@ "scripts": { "build:development": "cross-env RAILS_ENV=development NODE_ENV=development ./bin/webpack", "build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack", - "fix:js": "yarn lint:js --fix", - "fix:json": "prettier --write \"**/*.{json,json5}\"", - "fix:md": "prettier --write \"**/*.md\"", - "fix:sass": "stylelint --fix \"**/*.{css,scss}\" && prettier --write \"**/*.{css,scss}\"", - "fix:yml": "prettier --write \"**/*.{yaml,yml}\"", - "fix": "yarn fix:js && yarn fix:json && yarn fix:sass && yarn fix:yml", + "fix:js": "eslint . --ext=.js,.jsx,.ts,.tsx --cache --report-unused-disable-directives --fix", + "fix:css": "stylelint --fix \"**/*.{css,scss}\"", + "fix": "yarn fix:js && yarn fix:css", + "format": "prettier --write --log-level warn .", + "format:check": "prettier --check --ignore-unknown .", "i18n:extract": "formatjs extract 'app/javascript/**/*.{js,jsx,ts,tsx}' '--ignore=**/*.d.ts' --out-file app/javascript/mastodon/locales/en.json --format config/formatjs-formatter.js", "jest": "cross-env NODE_ENV=test jest", "lint:js": "eslint . --ext=.js,.jsx,.ts,.tsx --cache --report-unused-disable-directives", - "lint:json": "prettier --check \"**/*.{json,json5}\"", - "lint:md": "prettier --check \"**/*.md\"", - "lint:sass": "stylelint \"**/*.{css,scss}\" && prettier --check \"**/*.{css,scss}\"", - "lint:yml": "prettier --check \"**/*.{yaml,yml}\"", - "lint": "yarn lint:js && yarn lint:json && yarn lint:sass && yarn lint:yml", + "lint:css": "stylelint \"**/*.{css,scss}\"", + "lint": "yarn lint:js && yarn lint:css", "postversion": "git push --tags", "prepare": "husky", "start": "node ./streaming/index.js", @@ -177,14 +173,12 @@ "@typescript-eslint/parser": "^7.0.0", "babel-jest": "^29.5.0", "eslint": "^8.41.0", - "eslint-config-prettier": "^9.0.0", "eslint-define-config": "^2.0.0", "eslint-import-resolver-typescript": "^3.5.5", "eslint-plugin-formatjs": "^4.10.1", "eslint-plugin-import": "~2.29.0", "eslint-plugin-jsdoc": "^48.0.0", "eslint-plugin-jsx-a11y": "~6.8.0", - "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-promise": "~6.1.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", diff --git a/yarn.lock b/yarn.lock index 442473a0922298..dda4ed326e0cbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,7 +42,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.23.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" dependencies: @@ -373,6 +373,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.22.15": + version: 7.23.6 + resolution: "@babel/parser@npm:7.23.6" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/6f76cd5ccae1fa9bcab3525b0865c6222e9c1d22f87abc69f28c5c7b2c8816a13361f5bd06bddbd5faf903f7320a8feba02545c981468acec45d12a03db7755e + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.23.3": version: 7.23.3 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3" @@ -1491,7 +1500,18 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.22.15, @babel/template@npm:^7.23.9, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3": + version: 7.22.15 + resolution: "@babel/template@npm:7.22.15" + dependencies: + "@babel/code-frame": "npm:^7.22.13" + "@babel/parser": "npm:^7.22.15" + "@babel/types": "npm:^7.22.15" + checksum: 10c0/9312edd37cf1311d738907003f2aa321a88a42ba223c69209abe4d7111db019d321805504f606c7fd75f21c6cf9d24d0a8223104cd21ebd207e241b6c551f454 + languageName: node + linkType: hard + +"@babel/template@npm:^7.23.9": version: 7.23.9 resolution: "@babel/template@npm:7.23.9" dependencies: @@ -1520,7 +1540,18 @@ __metadata: languageName: node 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.6, @babel/types@npm:^7.23.9, @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.6 + resolution: "@babel/types@npm:7.23.6" + dependencies: + "@babel/helper-string-parser": "npm:^7.23.4" + "@babel/helper-validator-identifier": "npm:^7.22.20" + to-fast-properties: "npm:^2.0.0" + checksum: 10c0/42cefce8a68bd09bb5828b4764aa5586c53c60128ac2ac012e23858e1c179347a4aac9c66fc577994fbf57595227611c5ec8270bf0cfc94ff033bbfac0550b70 + languageName: node + linkType: hard + +"@babel/types@npm:^7.23.9": version: 7.23.9 resolution: "@babel/types@npm:7.23.9" dependencies: @@ -2360,14 +2391,12 @@ __metadata: emoji-mart: "npm:emoji-mart-lazyload@latest" escape-html: "npm:^1.0.3" eslint: "npm:^8.41.0" - eslint-config-prettier: "npm:^9.0.0" eslint-define-config: "npm:^2.0.0" eslint-import-resolver-typescript: "npm:^3.5.5" eslint-plugin-formatjs: "npm:^4.10.1" eslint-plugin-import: "npm:~2.29.0" eslint-plugin-jsdoc: "npm:^48.0.0" eslint-plugin-jsx-a11y: "npm:~6.8.0" - eslint-plugin-prettier: "npm:^5.0.0" eslint-plugin-promise: "npm:~6.1.1" eslint-plugin-react: "npm:^7.33.2" eslint-plugin-react-hooks: "npm:^4.6.0" @@ -2572,20 +2601,6 @@ __metadata: languageName: node linkType: hard -"@pkgr/utils@npm:^2.4.2": - version: 2.4.2 - resolution: "@pkgr/utils@npm:2.4.2" - dependencies: - cross-spawn: "npm:^7.0.3" - fast-glob: "npm:^3.3.0" - is-glob: "npm:^4.0.3" - open: "npm:^9.1.0" - picocolors: "npm:^1.0.0" - tslib: "npm:^2.6.0" - checksum: 10c0/7c3e68f6405a1d4c51f418d8d580e71d7bade2683d5db07e8413d8e57f7e389047eda44a2341f77a1b3085895fca7676a9d45e8812a58312524f8c4c65d501be - languageName: node - linkType: hard - "@polka/url@npm:^1.0.0-next.20": version: 1.0.0-next.21 resolution: "@polka/url@npm:1.0.0-next.21" @@ -4939,13 +4954,6 @@ __metadata: languageName: node linkType: hard -"big-integer@npm:^1.6.44": - version: 1.6.51 - resolution: "big-integer@npm:1.6.51" - checksum: 10c0/c8139662d57f8833a44802f4b65be911679c569535ea73c5cfd3c1c8994eaead1b84b6f63e1db63833e4d4cacb6b6a9e5522178113dfdc8e4c81ed8436f1e8cc - languageName: node - linkType: hard - "big.js@npm:^5.2.2": version: 5.2.2 resolution: "big.js@npm:5.2.2" @@ -5059,15 +5067,6 @@ __metadata: languageName: node linkType: hard -"bplist-parser@npm:^0.2.0": - version: 0.2.0 - resolution: "bplist-parser@npm:0.2.0" - dependencies: - big-integer: "npm:^1.6.44" - checksum: 10c0/ce79c69e0f6efe506281e7c84e3712f7d12978991675b6e3a58a295b16f13ca81aa9b845c335614a545e0af728c8311b6aa3142af76ba1cb616af9bbac5c4a9f - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -5290,15 +5289,6 @@ __metadata: languageName: node linkType: hard -"bundle-name@npm:^3.0.0": - version: 3.0.0 - resolution: "bundle-name@npm:3.0.0" - dependencies: - run-applescript: "npm:^5.0.0" - checksum: 10c0/57bc7f8b025d83961b04db2f1eff6a87f2363c2891f3542a4b82471ff8ebb5d484af48e9784fcdb28ef1d48bb01f03d891966dc3ef58758e46ea32d750ce40f8 - languageName: node - linkType: hard - "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -6524,28 +6514,6 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:^3.0.0": - version: 3.0.0 - resolution: "default-browser-id@npm:3.0.0" - dependencies: - bplist-parser: "npm:^0.2.0" - untildify: "npm:^4.0.0" - checksum: 10c0/8db3ab882eb3e1e8b59d84c8641320e6c66d8eeb17eb4bb848b7dd549b1e6fd313988e4a13542e95fbaeff03f6e9dedc5ad191ad4df7996187753eb0d45c00b7 - languageName: node - linkType: hard - -"default-browser@npm:^4.0.0": - version: 4.0.0 - resolution: "default-browser@npm:4.0.0" - dependencies: - bundle-name: "npm:^3.0.0" - default-browser-id: "npm:^3.0.0" - execa: "npm:^7.1.1" - titleize: "npm:^3.0.0" - checksum: 10c0/7c8848badc139ecf9d878e562bc4e7ab4301e51ba120b24d8dcb14739c30152115cc612065ac3ab73c02aace4afa29db5a044257b2f0cf234f16e3a58f6c925e - languageName: node - linkType: hard - "default-gateway@npm:^4.2.0": version: 4.2.0 resolution: "default-gateway@npm:4.2.0" @@ -6567,13 +6535,6 @@ __metadata: languageName: node linkType: hard -"define-lazy-prop@npm:^3.0.0": - version: 3.0.0 - resolution: "define-lazy-prop@npm:3.0.0" - checksum: 10c0/5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49 - languageName: node - linkType: hard - "define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" @@ -7298,17 +7259,6 @@ __metadata: languageName: node linkType: hard -"eslint-config-prettier@npm:^9.0.0": - version: 9.1.0 - resolution: "eslint-config-prettier@npm:9.1.0" - peerDependencies: - eslint: ">=7.0.0" - bin: - eslint-config-prettier: bin/cli.js - checksum: 10c0/6d332694b36bc9ac6fdb18d3ca2f6ac42afa2ad61f0493e89226950a7091e38981b66bac2b47ba39d15b73fff2cd32c78b850a9cf9eed9ca9a96bfb2f3a2f10d - languageName: node - linkType: hard - "eslint-define-config@npm:^2.0.0": version: 2.1.0 resolution: "eslint-define-config@npm:2.1.0" @@ -7450,26 +7400,6 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-prettier@npm:^5.0.0": - version: 5.1.3 - resolution: "eslint-plugin-prettier@npm:5.1.3" - dependencies: - prettier-linter-helpers: "npm:^1.0.0" - synckit: "npm:^0.8.6" - peerDependencies: - "@types/eslint": ">=8.0.0" - eslint: ">=8.0.0" - eslint-config-prettier: "*" - prettier: ">=3.0.0" - peerDependenciesMeta: - "@types/eslint": - optional: true - eslint-config-prettier: - optional: true - checksum: 10c0/f45d5fc1fcfec6b0cf038a7a65ddd10a25df4fe3f9e1f6b7f0d5100e66f046a26a2492e69ee765dddf461b93c114cf2e1eb18d4970aafa6f385448985c136e09 - languageName: node - linkType: hard - "eslint-plugin-promise@npm:~6.1.1": version: 6.1.1 resolution: "eslint-plugin-promise@npm:6.1.1" @@ -7758,23 +7688,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^7.1.1": - version: 7.2.0 - resolution: "execa@npm:7.2.0" - dependencies: - cross-spawn: "npm:^7.0.3" - get-stream: "npm:^6.0.1" - human-signals: "npm:^4.3.0" - is-stream: "npm:^3.0.0" - merge-stream: "npm:^2.0.0" - npm-run-path: "npm:^5.1.0" - onetime: "npm:^6.0.0" - signal-exit: "npm:^3.0.7" - strip-final-newline: "npm:^3.0.0" - checksum: 10c0/098cd6a1bc26d509e5402c43f4971736450b84d058391820c6f237aeec6436963e006fd8423c9722f148c53da86aa50045929c7278b5522197dff802d10f9885 - languageName: node - linkType: hard - "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -7914,14 +7827,7 @@ __metadata: languageName: node linkType: hard -"fast-diff@npm:^1.1.2": - version: 1.3.0 - resolution: "fast-diff@npm:1.3.0" - checksum: 10c0/5c19af237edb5d5effda008c891a18a585f74bf12953be57923f17a3a4d0979565fc64dbc73b9e20926b9d895f5b690c618cbb969af0cf022e3222471220ad29 - languageName: node - linkType: hard - -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.1, fast-glob@npm:^3.3.2": +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.1, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -8432,7 +8338,7 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": +"get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 @@ -9032,13 +8938,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^4.3.0": - version: 4.3.1 - resolution: "human-signals@npm:4.3.1" - checksum: 10c0/40498b33fe139f5cc4ef5d2f95eb1803d6318ac1b1c63eaf14eeed5484d26332c828de4a5a05676b6c83d7b9e57727c59addb4b1dea19cb8d71e83689e5b336c - languageName: node - linkType: hard - "human-signals@npm:^5.0.0": version: 5.0.0 resolution: "human-signals@npm:5.0.0" @@ -9512,24 +9411,6 @@ __metadata: languageName: node linkType: hard -"is-docker@npm:^2.0.0": - version: 2.2.1 - resolution: "is-docker@npm:2.2.1" - bin: - is-docker: cli.js - checksum: 10c0/e828365958d155f90c409cdbe958f64051d99e8aedc2c8c4cd7c89dcf35329daed42f7b99346f7828df013e27deb8f721cf9408ba878c76eb9e8290235fbcdcc - languageName: node - linkType: hard - -"is-docker@npm:^3.0.0": - version: 3.0.0 - resolution: "is-docker@npm:3.0.0" - bin: - is-docker: cli.js - checksum: 10c0/d2c4f8e6d3e34df75a5defd44991b6068afad4835bb783b902fa12d13ebdb8f41b2a199dcb0b5ed2cb78bfee9e4c0bbdb69c2d9646f4106464674d3e697a5856 - languageName: node - linkType: hard - "is-electron@npm:^2.2.0": version: 2.2.2 resolution: "is-electron@npm:2.2.2" @@ -9633,17 +9514,6 @@ __metadata: languageName: node linkType: hard -"is-inside-container@npm:^1.0.0": - version: 1.0.0 - resolution: "is-inside-container@npm:1.0.0" - dependencies: - is-docker: "npm:^3.0.0" - bin: - is-inside-container: cli.js - checksum: 10c0/a8efb0e84f6197e6ff5c64c52890fa9acb49b7b74fed4da7c95383965da6f0fa592b4dbd5e38a79f87fc108196937acdbcd758fcefc9b140e479b39ce1fcd1cd - languageName: node - linkType: hard - "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -9887,15 +9757,6 @@ __metadata: languageName: node linkType: hard -"is-wsl@npm:^2.2.0": - version: 2.2.0 - resolution: "is-wsl@npm:2.2.0" - dependencies: - is-docker: "npm:^2.0.0" - checksum: 10c0/a6fa2d370d21be487c0165c7a440d567274fbba1a817f2f0bfa41cc5e3af25041d84267baa22df66696956038a43973e72fca117918c91431920bdef490fa25e - languageName: node - linkType: hard - "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -12146,18 +12007,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^9.1.0": - version: 9.1.0 - resolution: "open@npm:9.1.0" - dependencies: - default-browser: "npm:^4.0.0" - define-lazy-prop: "npm:^3.0.0" - is-inside-container: "npm:^1.0.0" - is-wsl: "npm:^2.2.0" - checksum: 10c0/8073ec0dd8994a7a7d9bac208bd17d093993a65ce10f2eb9b62b6d3a91c9366ae903938a237c275493c130171d339f6dcbdd2a2de7e32953452c0867b97825af - languageName: node - linkType: hard - "opencollective-postinstall@npm:^2.0.2": version: 2.0.3 resolution: "opencollective-postinstall@npm:2.0.3" @@ -13285,15 +13134,6 @@ __metadata: languageName: node linkType: hard -"prettier-linter-helpers@npm:^1.0.0": - version: 1.0.0 - resolution: "prettier-linter-helpers@npm:1.0.0" - dependencies: - fast-diff: "npm:^1.1.2" - checksum: 10c0/81e0027d731b7b3697ccd2129470ed9913ecb111e4ec175a12f0fcfab0096516373bf0af2fef132af50cafb0a905b74ff57996d615f59512bb9ac7378fcc64ab - languageName: node - linkType: hard - "prettier@npm:^3.0.0": version: 3.2.5 resolution: "prettier@npm:3.2.5" @@ -14553,15 +14393,6 @@ __metadata: languageName: node linkType: hard -"run-applescript@npm:^5.0.0": - version: 5.0.0 - resolution: "run-applescript@npm:5.0.0" - dependencies: - execa: "npm:^5.0.0" - checksum: 10c0/f9977db5770929f3f0db434b8e6aa266498c70dec913c84320c0a06add510cf44e3a048c44da088abee312006f9cbf572fd065cdc8f15d7682afda8755f4114c - languageName: node - linkType: hard - "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -15960,16 +15791,6 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.8.6": - version: 0.8.6 - resolution: "synckit@npm:0.8.6" - dependencies: - "@pkgr/utils": "npm:^2.4.2" - tslib: "npm:^2.6.2" - checksum: 10c0/200528062e3915a0190a4c6b1e01436fcfdf812e2e8d977746746f3998bb4182d758af760e51b06a64f8323e705735aff7b4b3efc4a0ab5f75eaccc044a8cfcc - languageName: node - linkType: hard - "table@npm:^6.8.1": version: 6.8.1 resolution: "table@npm:6.8.1" @@ -16164,13 +15985,6 @@ __metadata: languageName: node linkType: hard -"titleize@npm:^3.0.0": - version: 3.0.0 - resolution: "titleize@npm:3.0.0" - checksum: 10c0/5ae6084ba299b5782f95e3fe85ea9f0fa4d74b8ae722b6b3208157e975589fbb27733aeba4e5080fa9314a856044ef52caa61b87caea4b1baade951a55c06336 - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -16313,7 +16127,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.2, tslib@npm:^2.4.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2": +"tslib@npm:2.6.2, tslib@npm:^2.4.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 10c0/e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb @@ -16635,13 +16449,6 @@ __metadata: languageName: node linkType: hard -"untildify@npm:^4.0.0": - version: 4.0.0 - resolution: "untildify@npm:4.0.0" - checksum: 10c0/d758e624c707d49f76f7511d75d09a8eda7f2020d231ec52b67ff4896bcf7013be3f9522d8375f57e586e9a2e827f5641c7e06ee46ab9c435fc2b2b2e9de517a - languageName: node - linkType: hard - "upath@npm:^1.1.1, upath@npm:^1.2.0": version: 1.2.0 resolution: "upath@npm:1.2.0" From ea8e7f3e9d994189bfa3f2e3486fffad9c22adb1 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 27 Feb 2024 11:25:12 -0500 Subject: [PATCH 13/18] Align `TagServersMeasure` query style with other classes (#29414) --- app/lib/admin/metrics/measure/tag_servers_measure.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/admin/metrics/measure/tag_servers_measure.rb b/app/lib/admin/metrics/measure/tag_servers_measure.rb index e6378b8021c6e7..6eb46bca456b06 100644 --- a/app/lib/admin/metrics/measure/tag_servers_measure.rb +++ b/app/lib/admin/metrics/measure/tag_servers_measure.rb @@ -34,10 +34,10 @@ def sql_query_string INNER JOIN accounts ON statuses.account_id = accounts.id WHERE statuses_tags.tag_id = :tag_id AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id - AND date_trunc('day', statuses.created_at)::date = axis.day + AND date_trunc('day', statuses.created_at)::date = axis.period ) FROM ( - SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, ('1 day')::interval) AS day + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) as axis SQL end From 8f3c91fc3c8ff4b150b171c7b3287a8b65e6dd07 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 27 Feb 2024 11:25:58 -0500 Subject: [PATCH 14/18] Add `change` block expectation to `admin/invites#deactivate_all` spec (#29412) --- spec/controllers/admin/invites_controller_spec.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spec/controllers/admin/invites_controller_spec.rb b/spec/controllers/admin/invites_controller_spec.rb index c8f566f68ba754..71748cbbec2122 100644 --- a/spec/controllers/admin/invites_controller_spec.rb +++ b/spec/controllers/admin/invites_controller_spec.rb @@ -44,14 +44,13 @@ end describe 'POST #deactivate_all' do - it 'expires all invites, then redirects to admin_invites_path' do - invites = Fabricate.times(1, :invite, expires_at: nil) - - post :deactivate_all + before { Fabricate(:invite, expires_at: nil) } - invites.each do |invite| - expect(invite.reload).to be_expired - end + it 'expires all invites, then redirects to admin_invites_path' do + expect { post :deactivate_all } + .to change { Invite.exists?(expires_at: nil) } + .from(true) + .to(false) expect(response).to redirect_to admin_invites_path end From edfc53856f03e3948c2c6760bb425f74dbb24178 Mon Sep 17 00:00:00 2001 From: Roni Laukkarinen Date: Wed, 28 Feb 2024 14:01:18 +0200 Subject: [PATCH 15/18] Fix the regression with the search icon position (#29417) --- app/javascript/styles/mastodon/components.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 93f2c0a48bb131..29c91ef7bc6f82 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5204,6 +5204,7 @@ a.status-card { color: $darker-text-color; cursor: default; pointer-events: none; + margin-inline-start: 16px - 2px; &.active { pointer-events: auto; From deffb8ecb6d2eb384e4d9237f4918be664f21cf9 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Wed, 28 Feb 2024 21:07:06 +0900 Subject: [PATCH 16/18] Show comments in the admin/instances page (#29240) Co-authored-by: Claire --- app/javascript/styles/mastodon/admin.scss | 17 +++++++++++++++++ app/views/admin/instances/_instance.html.haml | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index fcd630c23c9ecc..ef9c2fd88966ba 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -324,6 +324,23 @@ $content-width: 840px; padding-bottom: 0; margin-bottom: 0; border-bottom: 0; + + .comment { + display: block; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 4px; + + &.private-comment { + display: block; + color: $darker-text-color; + } + + &.public-comment { + display: block; + color: $secondary-text-color; + } + } } & > p { diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml index 65cf789ce316df..522a2444bb9d6b 100644 --- a/app/views/admin/instances/_instance.html.haml +++ b/app/views/admin/instances/_instance.html.haml @@ -7,6 +7,10 @@ %small - if instance.domain_block = instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ') + - if instance.domain_block.public_comment.present? + %span.comment.public-comment #{t('admin.domain_blocks.public_comment')}: #{instance.domain_block.public_comment} + - if instance.domain_block.private_comment.present? + %span.comment.private-comment #{t('admin.domain_blocks.private_comment')}: #{instance.domain_block.private_comment} - elsif instance.domain_allow = t('admin.accounts.whitelisted') - else From 7f84bbfd92377689f84121ec45c5b9d809d94cde Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:14:42 +0100 Subject: [PATCH 17/18] New Crowdin Translations (automated) (#29430) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/eu.json | 2 +- app/javascript/mastodon/locales/fa.json | 25 ++++++-- app/javascript/mastodon/locales/ko.json | 2 +- app/javascript/mastodon/locales/ms.json | 4 ++ config/locales/devise.fa.yml | 84 ++++++++++++++----------- config/locales/fa.yml | 5 +- config/locales/simple_form.fi.yml | 4 +- 7 files changed, 78 insertions(+), 48 deletions(-) diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index da7d03ce4199ea..c21fa1d204d998 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -500,7 +500,7 @@ "onboarding.share.message": "{username} naiz #Mastodon-en! Jarrai nazazu hemen: {url}", "onboarding.share.next_steps": "Hurrengo urrats posibleak:", "onboarding.share.title": "Partekatu zure profila", - "onboarding.start.lead": "Zure Mastodoneko kontu berria prest dago. Jakin nola atera diezaioekun etekin handiena hemen:", + "onboarding.start.lead": "Mastodonen parte zara orain, bakarra eta deszentralizatua den sare-sozialaren plataforma, non zuk, eta ez algoritmo batek, zeure esperientzia pertsonaliza dezakezun. Igaro ezazu muga soziala:", "onboarding.start.skip": "Urrats guztiak saltatu nahi dituzu?", "onboarding.start.title": "Lortu duzu!", "onboarding.steps.follow_people.body": "Zure jarioa zuk pertsonalizatzen duzu. Bete dezagun jende interesgarriaz.", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 4050ca9f9f7b8f..9029ba3cb5c3cf 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -110,7 +110,7 @@ "column.about": "درباره", "column.blocks": "کاربران مسدود شده", "column.bookmarks": "نشانک‌ها", - "column.community": "خط زمانی محلّی", + "column.community": "خط زمانی محلی", "column.direct": "اشاره‌های خصوصی", "column.directory": "مرور نمایه‌ها", "column.domain_blocks": "دامنه‌های مسدود شده", @@ -131,7 +131,7 @@ "column_header.show_settings": "نمایش تنظیمات", "column_header.unpin": "برداشتن سنجاق", "column_subheading.settings": "تنظیمات", - "community.column_settings.local_only": "فقط محلّی", + "community.column_settings.local_only": "فقط محلی", "community.column_settings.media_only": "فقط رسانه", "community.column_settings.remote_only": "تنها دوردست", "compose.language.change": "تغییر زبان", @@ -228,7 +228,7 @@ "empty_column.account_unavailable": "نمایهٔ موجود نیست", "empty_column.blocks": "هنوز کسی را مسدود نکرده‌اید.", "empty_column.bookmarked_statuses": "هنوز هیچ فرستهٔ نشانه‌گذاری شده‌ای ندارید. هنگامی که فرسته‌ای را نشانه‌گذاری کنید، این‌جا نشان داده خواهد شد.", - "empty_column.community": "خط زمانی محلّی خالی است. چیزی بنویسید تا چرخش بچرخد!", + "empty_column.community": "خط زمانی محلی خالیست. چیزی نوشته تا چرخش بچرخد!", "empty_column.direct": "هنوز هیچ اشاره خصوصی‌ای ندارید. هنگامی که چنین پیامی بگیرید یا بفرستید این‌جا نشان داده خواهد شد.", "empty_column.domain_blocks": "هنوز هیچ دامنه‌ای مسدود نشده است.", "empty_column.explore_statuses": "الآن چیزی پرطرفدار نیست. بعداً دوباره بررسی کنید!", @@ -277,6 +277,17 @@ "follow_request.authorize": "اجازه دهید", "follow_request.reject": "رد کنید", "follow_requests.unlocked_explanation": "با این که حسابتان قفل نیست، کارکنان {domain} فکر کردند که ممکن است بخواهید درخواست‌ها از این حساب‌ها را به صورت دستی بازبینی کنید.", + "follow_suggestions.curated_suggestion": "گزینش سردبیر", + "follow_suggestions.dismiss": "دیگر نشان داده نشود", + "follow_suggestions.hints.featured": "این نمایه به دست گروه {domain} دستچین شده.", + "follow_suggestions.hints.friends_of_friends": "این نمایه بین کسانی که پی می‌گیرید محبوب است.", + "follow_suggestions.hints.most_followed": "این نمایه روی {domain} بسیار پی‌گرفته شده.", + "follow_suggestions.hints.most_interactions": "این نمایه اخیراُ روی {domain} توجّه زیادی گرفته.", + "follow_suggestions.hints.similar_to_recently_followed": "این نمایه شبیه نمایه‌هاییست که اخیراً پی‌گرفته‌اید.", + "follow_suggestions.personalized_suggestion": "پیشنهاد شخصی", + "follow_suggestions.popular_suggestion": "پیشنهاد محبوب", + "follow_suggestions.view_all": "دیدن همه", + "follow_suggestions.who_to_follow": "افرادی برای پی‌گیری", "followed_tags": "برچسب‌های پی‌گرفته", "footer.about": "درباره", "footer.directory": "فهرست نمایه‌ها", @@ -345,7 +356,7 @@ "keyboard_shortcuts.home": "گشودن خط زمانی خانگی", "keyboard_shortcuts.hotkey": "میان‌بر", "keyboard_shortcuts.legend": "نمایش این نشانه", - "keyboard_shortcuts.local": "گشودن خط زمانی محلّی", + "keyboard_shortcuts.local": "گشودن خط زمانی محلی", "keyboard_shortcuts.mention": "اشاره به نویسنده", "keyboard_shortcuts.muted": "گشودن فهرست کاربران خموش", "keyboard_shortcuts.my_profile": "گشودن نمایه‌تان", @@ -396,7 +407,7 @@ "navigation_bar.advanced_interface": "بازکردن در رابط کاربری وب پیشرفته", "navigation_bar.blocks": "کاربران مسدود شده", "navigation_bar.bookmarks": "نشانک‌ها", - "navigation_bar.community_timeline": "خط زمانی محلّی", + "navigation_bar.community_timeline": "خط زمانی محلی", "navigation_bar.compose": "نوشتن فرستهٔ تازه", "navigation_bar.direct": "اشاره‌های خصوصی", "navigation_bar.discover": "گشت و گذار", @@ -475,8 +486,10 @@ "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.profile.discoverable": "نمایه خود را قابل نمایش کنید", + "onboarding.profile.discoverable_hint": "خواسته‌اید روی ماستودون کشف شوید. ممکن است فرسته‌هایتان در نتیحهٔ جست‌وجوها و فرسته‌های داغ ظاهر شده و نمایه‌تان به افرادی با علایق مشابهتان پیشنهاد شود.", "onboarding.profile.display_name": "نام نمایشی", "onboarding.profile.display_name_hint": "نام کامل یا نام باحالتان…", + "onboarding.profile.lead": "همواره می‌توانید این مورد را در تنظیمات که گزینه‌ّای شخصی سازی بیش‌تری نیز دارد کامل کنید.", "onboarding.profile.note": "درباره شما", "onboarding.profile.note_hint": "می‌توانید افراد دیگر را @نام‌بردن یا #برچسب بزنید…", "onboarding.profile.save_and_continue": "ذخیره کن و ادامه بده", @@ -522,6 +535,7 @@ "privacy.private.short": "پی‌گیرندگان", "privacy.public.long": "هرکسی در و بیرون از ماستودون", "privacy.public.short": "عمومی", + "privacy.unlisted.additional": "درست مثل عمومی رفتار می‌کند؛ جز این که فرسته در برچسب‌ها یا خوراک‌های زنده، کشف یا جست‌وجوی ماستودون ظاهر نخواهد شد. حتا اگر کلیّت نمایه‌تان اجازه داده باشد.", "privacy.unlisted.long": "سروصدای الگوریتمی کم‌تر", "privacy.unlisted.short": "عمومی ساکت", "privacy_policy.last_updated": "آخرین به‌روز رسانی در {date}", @@ -541,6 +555,7 @@ "relative_time.minutes": "{number} دقیقه", "relative_time.seconds": "{number} ثانیه", "relative_time.today": "امروز", + "reply_indicator.attachments": "{count, plural, one {# پیوست} other {# پیوست}}", "reply_indicator.cancel": "لغو", "reply_indicator.poll": "نظرسنجی", "report.block": "انسداد", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 36290cbb95d8c6..d9192e5c61b046 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -212,7 +212,7 @@ "emoji_button.custom": "사용자 지정", "emoji_button.flags": "깃발", "emoji_button.food": "음식과 마실것", - "emoji_button.label": "에모지를 추가", + "emoji_button.label": "에모지 추가", "emoji_button.nature": "자연", "emoji_button.not_found": "해당하는 에모지가 없습니다", "emoji_button.objects": "물건", diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index 88b4680ae0fb6b..3d6ba920c97765 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -277,7 +277,11 @@ "follow_request.authorize": "Benarkan", "follow_request.reject": "Tolak", "follow_requests.unlocked_explanation": "Walaupun akaun anda tidak dikunci, kakitangan {domain} merasakan anda mungkin ingin menyemak permintaan ikutan daripada akaun ini secara manual.", + "follow_suggestions.curated_suggestion": "", "follow_suggestions.dismiss": "Jangan papar lagi", + "follow_suggestions.hints.featured": "Profil{domain.", + "follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.", + "follow_suggestions.hints.most_followed": ".", "follow_suggestions.personalized_suggestion": "Cadangan peribadi", "follow_suggestions.popular_suggestion": "Cadangan terkenal", "follow_suggestions.view_all": "Lihat semua", diff --git a/config/locales/devise.fa.yml b/config/locales/devise.fa.yml index d4198626d4982a..c441e346a24b44 100644 --- a/config/locales/devise.fa.yml +++ b/config/locales/devise.fa.yml @@ -2,16 +2,17 @@ fa: devise: confirmations: - confirmed: نشانی ایمیل شما با موفقیت تأیید شد. - send_instructions: تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید نشانی ایمیل خود را تأیید کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. - send_paranoid_instructions: اگر ایمیل شما در پایگاه دادهٔ ما موجود باشد، تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید نشانی ایمیل خود را تأیید کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. + confirmed: نشانی رایانامه‌تان با موفقیت تأیید شد. + send_instructions: تا دقایقی دیگر رایانامه‌ای با دستورالعمل تأیید نشانی رایانامه‌تان دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. + send_paranoid_instructions: اگر نشانی رایانامه‌تان در پایگاه داده‌مان وجود داشته باشد، تا دقایقی دیگر تا دقایقی دیگر رایانامه‌ای با دستورالعمل تأیید نشانی رایانامه‌تان دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. failure: - already_authenticated: همین الآن هم وارد شده‌اید. - inactive: حساب شما هنوز فعال نشده است. + already_authenticated: از پیش وارد شده‌اید. + inactive: هنوز حسابتان فعّال نشده. invalid: "%{authentication_keys} یا گذرواژه نامعتبر." last_attempt: پیش از آن که حساب شما قفل شود، یک فرصت دیگر دارید. - locked: حساب شما قفل شده است. + locked: حسابتان قفل شده. not_found_in_database: "%{authentication_keys} یا گذرواژه نامعتبر." + omniauth_user_creation_failure: خطای ایجاد حسابی برای این هویت. pending: حساب شما همچنان در دست بررسی است. timeout: مهلت این ورود شما به سر رسید. برای ادامه، دوباره وارد شوید. unauthenticated: برای ادامه باید وارد شوید یا ثبت نام کنید. @@ -24,37 +25,42 @@ fa: explanation_when_pending: شما با این نشانی ایمیل برای %{host} درخواست دعوت‌نامه داده‌اید. اگر ایمیل خود را تأیید کنید، ما درخواست شما را بررسی خواهیم کرد. تا وقتی بررسی تمام نشده، شما نمی‌توانید به حساب خود وارد شوید. اگر درخواست شما رد شود، ما اطلاعاتی را که از شما داریم پاک خواهیم کرد پس نیازی به کاری از سمت شما نخواهد بود. اگر شما چنین درخواستی نداده‌اید، لطفاً این ایمیل را نادیده بگیرید. extra_html: لطفاً همچنین قوانین کارساز و شرایط خدمتمان را بررسی کنید. subject: 'ماستودون: دستورالعمل تأیید برای %{instance}' - title: تأیید نشانی ایمیل + title: تأیید نشانی رایانامه email_changed: - explanation: 'نشانی ایمیل حساب شما تغییر می‌کند به:' - extra: اگر شما ایمیل خود را عوض نکردید، شاید کسی به حساب شما دسترسی پیدا کرده است. در این صورت لطفاً هر چه زودتر گذرواژه حسابتان را عوض کنید. اگر گذرواژه‌تان دیگر کار نمی‌کند، لطفاً با مدیر سرور تماس بگیرید. - subject: 'ماستودون: نشانی ایمیل عوض شد' - title: نشانی ایمیل تازه + explanation: 'نشانی رایانامهٔ حسابتان تغییر می‌کند به:' + extra: اگر رایانامه‌تان را عوض نکرده‌اید، ممکن است کسی به حسابتان دسترسی پیدا کرده باشد. لطفاً فوراُ گذرواژه‌تان را عوض کرده و اگر از حسابتان بیرون مانده‌اید با مدیر کارساز تماس بگیرید. + subject: 'ماستودون: رایانامه عوض شد' + title: نشانی جدید رایانامه password_change: - explanation: گذرواژه حساب شما تغییر کرد. - extra: اگر شما گذرواژه حسابتان را تغییر ندادید، شاید کسی به حساب شما دسترسی پیدا کرده است. در این صورت لطفاً هر چه زودتر گذرواژه حسابتان را عوض کنید. اگر گذرواژه‌تان دیگر کار نمی‌کند، لطفاً با مدیر سرور تماس بگیرید. - subject: 'ماستودون: گذرواژه‌تان عوض شد' - title: گذرواژه‌تان عوض شد + explanation: گذرواژهٔ حسابتان عوض شده. + extra: اگر گذرواژه‌تان را عوض نکرده‌اید، ممکن است کسی به حسابتان دسترسی پیدا کرده باشد. لطفاً فوراُ گذرواژه‌تان را عوض کرده و اگر از حسابتان بیرون مانده‌اید با مدیر کارساز تماس بگیرید. + subject: 'ماستودون: گذرواژه عوض شد' + title: گذرواژه عوض شد reconfirmation_instructions: - explanation: نشانی تازه را تأیید کنید تا ایمیل‌تان عوض شود. - extra: اگر شما باعث این تغییر نبودید، لطفاً این ایمیل را نادیده بگیرید. تا زمانی که شما پیوند بالا را باز نکنید، نشانی ایمیل مربوط به حساب شما عوض نخواهد شد. + explanation: برای تغییر رایانامه‌تان نشانی جدید را تأیید کنید. + extra: اگر خودتان چنین درخواستی نداده‌اید لطفاً از این رایانامه چشم بپوشید. نشانی رایانامهٔ حساب ماستودون تا وقتی به پیوند بالا دسترسی پیدا نکنید عوض نخواهد شد. subject: 'ماستودون: تأیید رایانامه برای %{instance}' - title: تأیید نشانی ایمیل + title: تأیید نشانی رایانامه reset_password_instructions: action: تغییر گذرواژه - explanation: شما گذرواژه تازه‌ای برای حسابتان درخواست کردید. - extra: اگر شما چنین درخواستی نکردید، لطفاً این ایمیل را نادیده بگیرید. تا زمانی که شما پیوند بالا را باز نکنید و گذرواژه تازه‌ای نسازید، گذرواژه شما عوض نخواهد شد. - subject: 'ماستودون: راهنمایی برای بازنشانی گذرواژه' + explanation: درخواست گذرواژه‌ای تازه‌ای برای حسابتان کرده‌اید. + extra: اگر خودتان چنین درخواستی نداده‌اید لطفاً از این رایانامه چشم بپوشید. گذرواژه‌تان تا وقتی به پیوند بالا دسترسی پیدا نکرده و گذرواژهٔ جدیدی نسازید عوض نخواهد شد. + subject: 'ماستودون: دستورالعمل‌های بازنشانی گذرواژه' title: بازنشانی گذرواژه two_factor_disabled: - subject: 'ماستودون: تأیید هویت دو مرحله‌ای از کار افتاد' - title: ورود دومرحله‌ای غیرفعال + explanation: ورود اکنون تنها با نشانی رایانامه و گذرواژه ممکن است. + subject: 'ماستودون: هویت‌سنجی دو مرحله‌ای از کار افتاده' + subtitle: هویت‌سنجی دو مرحله‌ای برای حسابتان از کار افتاده. + title: ورود دومرحله‌ای از کار افتاده two_factor_enabled: - subject: 'ماستودون: تأیید هویت دومرحله‌ای به کار افتاد' - title: ورود دومرحله‌ای فعال + explanation: ورود نیازمند ژتونی تولید شده به دست کارهٔ TOTP جفت‌شده است. + subject: 'ماستودون: هویت‌سنجی دومرحله‌ای به کار افتاده' + subtitle: هویت‌سنجی دو عاملی برای حسابتان به کار افتاده. + title: ورود دومرحله‌ای به کار افتاده two_factor_recovery_codes_changed: explanation: کدهای بازیابی پیشین نامعتبر شده و کدهای جدیدی ساخته شدند. subject: 'ماستودون: کدهای بازیابی برای تأیید هویت دو مرحله‌ای دوباره ساخته شدند' + subtitle: کدهای بازیابی پیشین از اعتبار ساقط شده و کدهایی جدید ایجاد شدند. title: کدهای بازیابی تأیید هویت دو مرحله‌ای عوض شده‌اند unlock_instructions: subject: 'ماستودون: دستورالعمل‌های قفل‌گشایی' @@ -68,9 +74,13 @@ fa: subject: 'ماستودون: کلید امنیتی حذف شد' title: یکی از کلیدهای امنیتیتان حذف شد webauthn_disabled: + explanation: هویت‌سنجی با کلیدهای امنیتی برای حسابتان از کار افتاده. + extra: ورود اکنون تنها با ژتون تولید شده به دست کارهٔ TOTP جفت‌شده ممکن است. subject: 'ماستودون: تأیید هویت با کلیدهای امنیتی از کار افتاد' title: کلیدهای امنیتی از کار افتادند webauthn_enabled: + explanation: هویت‌سنجی با کلیدهای امنیتی برای حسابتان به کار افتاده. + extra: کلید امنیتیتان اکنون می‌تواند برای ورود استفاده شود. subject: 'ماستودون: تأیید هویت با کلید امنیتی به کار افتاد' title: کلیدهای امنیتی به کار افتادند omniauth_callbacks: @@ -86,22 +96,22 @@ fa: destroyed: بدرود! حساب شما با موفقیت لغو شد. امیدواریم دوباره شما را ببینیم. signed_up: خوش آمدید! شما با موفقیت ثبت نام کردید. signed_up_but_inactive: خوش آمدید! با موفقیت ثبت نام کردید. ولی هنوز وارد نشده‌اید؛ چرا که حسابتان هنوز فعال نشده است. - signed_up_but_locked: خوش آمدید! با موفقیت ثبت نام کردید. ولی هنوز وارد نشده‌اید؛ چرا که حسابتان قفل است. - signed_up_but_pending: پیغامی که دارای یک پیوند برای تأیید است به نشانی ایمیل شما فرستاده شده. پس از این‌که پیوند را باز کردید، ما درخواست شما را بررسی خواهیم کرد. اگر درخواست شما پذیرفته شود، به شما خواهیم گفت. - signed_up_but_unconfirmed: پیامی با یک پیوند تأیید به نشانی ایمیل شما فرستاده شده. لطفاً پیوند موجود در ایمیل را دنبال کنید تا حسابتان فعال شود. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. - update_needs_confirmation: شما با موفقیت حسابتان را به‌روز کردید، ولی لازم است که ما نشانی ایمیل تازهٔ شما را تأیید کنیم. لطفاً ایمیل خود را ببینید و پیوند موجود در ایمیل را دنبال کنید تا تا نشانی ایمیل تازهٔ شما تأیید شود. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. - updated: حسابتان با موفقبت به‌روز شد. + signed_up_but_locked: با موفّقیت ثبت‌نام کرده‌اید. با این حال نمی‌توان واردتان کرد؛ چرا که حسابتان قفل است. + signed_up_but_pending: پیامی با پیوند تأیید به نشانی رایانامه‌تان فرستاده شده. پس از زدن پیوند درخواستتان را بازبینی خواهیم کرد. در صورت پذیرش آگاه خواهید شد. + signed_up_but_unconfirmed: پیامی با پیوند تأیید به نشانی رایانامه‌تان فرستاده شده. لطفاً برای فعّال کردن حسابتان پیوند را بزنید. اگر این رایانامه را نگرفته‌اید شاخهٔ هرزنامه‌ها را بررسی کنید. + update_needs_confirmation: حسابتان را با موفّقیت به‌روز کردید؛‌ ولی باید نشانی رایانامهٔ جدیتان را تأیید کنیم. لطفاً رایانامه‌تان را بررسی کرده و برای تأیید نشانی رایانهٔ جدیدتان پیوند را بزنید. اگر این رایانامه را نگرفته‌اید شاخهٔ هرزنامه‌ها را بررسی کنید. + updated: حسابتان با موفّقیت به‌روز شد. sessions: - already_signed_out: با موفقیت خارج شدید. - signed_in: با موفقیت وارد شدید. - signed_out: با موفقیت خارج شدید. + already_signed_out: با موفّقیت خارج شدید. + signed_in: با موفّقیت وارد شدید. + signed_out: با موفّقیت خارج شدید. unlocks: - send_instructions: تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید قفل حساب خود را باز کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. - send_paranoid_instructions: اگر حساب شما موجود باشد، تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید قفل آن را باز کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. - unlocked: قفل حساب شما با موفقیت باز شد. لطفاً برای ادامه وارد سیستم شوید. + send_instructions: تا دقایقی دیگر رایانامه‌ای با دستورالعمل قفل‌گشایی حسابتان دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. + send_paranoid_instructions: اگر حسابتان وجود داشته باشد تا دقایقی دیگر رایانامه‌ای با دستورالعمل قفل‌گشاییش دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید. + unlocked: حسابتان با موفّقیت قفل‌گشایی شد. لطفاً برای ادامه وارد شوید. errors: messages: - already_confirmed: تأیید شده، لطفاً وارد شوید + already_confirmed: از پیش تأیید شده. لطفاً ورود را بیازمایید confirmation_period_expired: باید ظرف %{period} تأیید شود، لطفاً دوباره درخواست دهید expired: مهلتش به سر رسید، لطفاً دوباره درخواست دهید not_found: پیدا نشد diff --git a/config/locales/fa.yml b/config/locales/fa.yml index bb3368dd9e54e1..f6ff3797455557 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -80,7 +80,7 @@ fa: joined: عضو شده در location: all: همه - local: محلّی + local: محلی remote: کارسازهای دیگر title: مکان login_status: وضعیت ورود @@ -415,13 +415,14 @@ fa: public_comment: یادداشت عمومی public_comment_hint: یادداشتی دربارهٔ محدودیت روی این دامین برای عموم، در صورتی که فهرست دامین‌های محدود شده منتشر شود. reject_media: نپذیرفتن پرونده‌های رسانه‌ای - reject_media_hint: پرونده‌های رسانه‌ای ذخیره‌شدهٔ محلّی را پاک کرده و از بارگیریشان در آینده خودداری می‌کند. بی‌تأثیر روی معلق‌ها + reject_media_hint: پرونده‌های رسانه‌ای ذخیره‌شدهٔ محلی را پاک کرده و از بارگیریشان در آینده خودداری می‌کند. بی‌تأثیر روی معلّق‌ها reject_reports: نپذیرفتن گزارش‌ها reject_reports_hint: گزارش‌هایی را که از این دامنه می‌آید نادیده می‌گیرد. بی‌تأثیر برای معلق‌شده‌ها undo: واگردانی مسدودسازی دامین view: دیدن مسدودسازی دامنه email_domain_blocks: add_new: افزودن تازه + allow_registrations_with_approval: اجازهٔ ثبت‌نام با تأیید attempts_over_week: one: "%{count} تلاش در هفتهٔ گذشته" other: "%{count} تلاش ورود در هفتهٔ گذشته" diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index d208feed6c5721..96bf11ef2de00f 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -39,14 +39,14 @@ fi: text: Voit valittaa varoituksesta vain kerran defaults: autofollow: Henkilöt, jotka rekisteröityvät kutsun kautta, seuraavat sinua automaattisesti - avatar: PNG, GIF tai JPG. Enintään %{size}. Skaalataan kokoon %{dimensions} px + avatar: WEBP, PNG, GIF tai JPG. Enintään %{size}. Skaalataan kokoon %{dimensions} px bot: Tämä tili suorittaa enimmäkseen automaattisia toimintoja eikä sitä ehkä valvota context: Ainakin yksi konteksti, jossa suodattimen pitäisi olla voimassa current_password: Turvallisuussyistä kirjoita nykyisen tilin salasana current_username: Vahvista kirjoittamalla nykyisen tilin käyttäjänimi digest: Lähetetään vain pitkän poissaolon jälkeen ja vain, jos olet saanut suoria viestejä poissaolosi aikana email: Sinulle lähetetään vahvistussähköposti - header: PNG, GIF tai JPG. Enintään %{size}. Skaalataan kokoon %{dimensions} px + header: WEBP, PNG, GIF tai JPG. Enintään %{size}. Skaalataan kokoon %{dimensions} px inbox_url: Kopioi URL-osoite haluamasi välittäjän etusivulta irreversible: Suodatetut julkaisut katoavat lopullisesti, vaikka suodatin poistettaisiin myöhemmin locale: Käyttöliittymän, sähköpostien ja puskuilmoitusten kieli From d47cb6cccd8077f661a1e850b39f20f17d58486f Mon Sep 17 00:00:00 2001 From: KMY Date: Thu, 29 Feb 2024 10:32:14 +0900 Subject: [PATCH 18/18] Fix test --- .bundler-audit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.bundler-audit.yml b/.bundler-audit.yml index 0671df390fed81..b8e59799069657 100644 --- a/.bundler-audit.yml +++ b/.bundler-audit.yml @@ -4,3 +4,4 @@ ignore: # We have rate-limits on authentication endpoints in place (including second # factor verification) since Mastodon v3.2.0 - CVE-2024-0227 + - CVE-2024-27456