From d3466948d1746b581f741ea895f8295f17d12bc7 Mon Sep 17 00:00:00 2001 From: Freeplay Date: Mon, 6 Feb 2023 15:52:00 -0500 Subject: [PATCH 0001/1248] =?UTF-8?q?Improving=20keyboard=20support,=20par?= =?UTF-8?q?t=201=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/client/src/components/MkButton.vue | 3 +- packages/client/src/components/form/radio.vue | 3 ++ .../client/src/components/form/switch.vue | 3 ++ packages/client/src/directives/tooltip.ts | 33 ++++++++++++------- packages/client/src/pages/admin/_header_.vue | 6 +--- packages/client/src/style.scss | 4 --- packages/client/src/ui/_common_/navbar.vue | 13 ++++++-- 7 files changed, 39 insertions(+), 26 deletions(-) diff --git a/packages/client/src/components/MkButton.vue b/packages/client/src/components/MkButton.vue index 9042500b4e..5220e8c954 100644 --- a/packages/client/src/components/MkButton.vue +++ b/packages/client/src/components/MkButton.vue @@ -184,8 +184,7 @@ function onMousedown(evt: MouseEvent): void { } &:focus-visible { - outline: solid 2px var(--focus); - outline-offset: 2px; + outline: auto; } &.inline { diff --git a/packages/client/src/components/form/radio.vue b/packages/client/src/components/form/radio.vue index b36f7e9fdc..4060385827 100644 --- a/packages/client/src/components/form/radio.vue +++ b/packages/client/src/components/form/radio.vue @@ -68,6 +68,9 @@ function toggle(): void { &:hover { border-color: var(--inputBorderHover) !important; } + &:focus-within { + outline: auto; + } &.checked { background-color: var(--accentedBg) !important; diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue index 1ed00ae655..ef717ab6f9 100644 --- a/packages/client/src/components/form/switch.vue +++ b/packages/client/src/components/form/switch.vue @@ -98,6 +98,9 @@ const toggle = () => { border-color: var(--inputBorderHover) !important; } } + &:focus-within > .button { + outline: auto; + } > .label { margin-left: 12px; diff --git a/packages/client/src/directives/tooltip.ts b/packages/client/src/directives/tooltip.ts index 7738d14e8c..91024a6e3d 100644 --- a/packages/client/src/directives/tooltip.ts +++ b/packages/client/src/directives/tooltip.ts @@ -76,23 +76,32 @@ export default { ev.preventDefault(); }); + function showTooltip() { + window.clearTimeout(self.showTimer); + window.clearTimeout(self.hideTimer); + self.showTimer = window.setTimeout(self.show, delay); + } + function hideTooltip() { + window.clearTimeout(self.showTimer); + window.clearTimeout(self.hideTimer); + self.hideTimer = window.setTimeout(self.close, delay); + } + el.addEventListener( - start, - () => { - window.clearTimeout(self.showTimer); - window.clearTimeout(self.hideTimer); - self.showTimer = window.setTimeout(self.show, delay); - }, + start, showTooltip, + { passive: true }, + ); + el.addEventListener( + "focusin", showTooltip, { passive: true }, ); el.addEventListener( - end, - () => { - window.clearTimeout(self.showTimer); - window.clearTimeout(self.hideTimer); - self.hideTimer = window.setTimeout(self.close, delay); - }, + end, hideTooltip, + { passive: true }, + ); + el.addEventListener( + "focusout", hideTooltip, { passive: true }, ); diff --git a/packages/client/src/pages/admin/_header_.vue b/packages/client/src/pages/admin/_header_.vue index bdb41b2d2c..12702790ec 100644 --- a/packages/client/src/pages/admin/_header_.vue +++ b/packages/client/src/pages/admin/_header_.vue @@ -265,11 +265,7 @@ onUnmounted(() => { font-weight: normal; opacity: 0.7; - &:hover { - opacity: 1; - } - - &.active { + &:hover, &:focus-visible, &.active { opacity: 1; } diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss index ca8bd8b435..01bff888a9 100644 --- a/packages/client/src/style.scss +++ b/packages/client/src/style.scss @@ -178,10 +178,6 @@ hr { pointer-events: none; } - &:focus-visible { - outline: none; - } - &:disabled { opacity: 0.5; cursor: default; diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue index c44c766b3c..e99656ed69 100644 --- a/packages/client/src/ui/_common_/navbar.vue +++ b/packages/client/src/ui/_common_/navbar.vue @@ -341,8 +341,6 @@ function more(ev: MouseEvent) { padding-left: 30px; line-height: 2.85rem; margin-bottom: 0.5rem; - text-overflow: ellipsis; - overflow: hidden; white-space: nowrap; width: 100%; text-align: left; @@ -368,6 +366,8 @@ function more(ev: MouseEvent) { > .text { position: relative; font-size: 0.9em; + overflow: hidden; + text-overflow: ellipsis; } &:hover { @@ -380,7 +380,7 @@ function more(ev: MouseEvent) { color: var(--navActive); } - &:hover, &.active { + &:hover, &:focus-within, &.active { color: var(--accent); transition: all 0.4s ease; @@ -562,5 +562,12 @@ function more(ev: MouseEvent) { } } } + + .item { + outline: none; + &:focus-visible:before { + outline: auto; + } + } } From 49ea503fe6a8167c4fa22ccff83c9971f493e928 Mon Sep 17 00:00:00 2001 From: Freeplay Date: Mon, 6 Feb 2023 23:23:56 -0500 Subject: [PATCH 0002/1248] Fix tabbing orders & allow closing modals w/ esc --- packages/client/src/components/MkMenu.vue | 14 +++++++------- packages/client/src/components/MkModal.vue | 8 ++++++-- packages/client/src/components/MkPopupMenu.vue | 2 +- packages/client/src/components/MkSuperMenu.vue | 6 +++--- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/client/src/components/MkMenu.vue b/packages/client/src/components/MkMenu.vue index 78c1ff2238..c9f8dbfd6d 100644 --- a/packages/client/src/components/MkMenu.vue +++ b/packages/client/src/components/MkMenu.vue @@ -12,33 +12,33 @@ {{ item.text }} - + - + {{ item.text }} - + {{ item.text }} - - + {{ item.text }} - - + + @@ -120,7 +133,6 @@ import XNoteSimple from "@/components/MkNoteSimple.vue"; import XMediaList from "@/components/MkMediaList.vue"; import XPoll from "@/components/MkPoll.vue"; import MkUrlPreview from "@/components/MkUrlPreview.vue"; -import XShowMoreButton from "./MkShowMoreButton.vue"; import XCwButton from "@/components/MkCwButton.vue"; import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm"; import { i18n } from "@/i18n"; @@ -145,7 +157,6 @@ const isLong = props.note.text != null && (props.note.text.split("\n").length > 9 || props.note.text.length > 500); const collapsed = $ref(props.note.cw == null && isLong); - const urls = props.note.text ? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5) : null; @@ -274,6 +285,43 @@ function focusFooter(ev) { top: 40px; } } + + :deep(.fade) { + display: block; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + > span { + display: inline-block; + background: var(--panel); + padding: 0.4em 1em; + font-size: 0.8em; + border-radius: 999px; + box-shadow: 0 2px 6px rgb(0 0 0 / 20%); + } + &:hover { + > span { + background: var(--panelHighlight); + } + } + } + } + + :deep(.showLess) { + width: 100%; + margin-top: 1em; + position: sticky; + bottom: var(--stickyBottom); + + > span { + display: inline-block; + background: var(--panel); + padding: 6px 10px; + font-size: 0.8em; + border-radius: 999px; + box-shadow: 0 0 7px 7px var(--bg); + } } } } diff --git a/packages/client/src/components/MkUserPreview.vue b/packages/client/src/components/MkUserPreview.vue index ddfd39f14a..1e6db14423 100644 --- a/packages/client/src/components/MkUserPreview.vue +++ b/packages/client/src/components/MkUserPreview.vue @@ -55,7 +55,20 @@ :custom-emojis="user.emojis" /> - + +
Date: Sat, 29 Apr 2023 19:39:28 -0700 Subject: [PATCH 0189/1248] 9h --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 300ff61217..454ba4d30e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "calckey", - "version": "13.2.0-beta9", + "version": "13.2.0-beta9h", "codename": "aqua", "repository": { "type": "git", From 4706bc8796ab04dc887a01079f72f7df9eda3da8 Mon Sep 17 00:00:00 2001 From: Laker Turner Date: Sat, 29 Apr 2023 10:28:49 +0000 Subject: [PATCH 0190/1248] chore: Translated using Weblate (English) Currently translated at 100.0% (1735 of 1735 strings) Translation: Calckey/locales Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ --- locales/en-US.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 2feb2cd947..f9d4d23f02 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1042,7 +1042,7 @@ moveFromLabel: "Account you're moving from:" moveFromDescription: "This will set an alias of your old account so that you can move\ \ from that account to this current one. Do this BEFORE moving from your older account.\ \ Please enter the tag of the account formatted like @person@instance.com" -migrationConfirm: "Are you absolutely sure you want to migrate your acccount to {account}?\ +migrationConfirm: "Are you absolutely sure you want to migrate your account to {account}?\ \ Once you do this, you won't be able to reverse it, and you won't be able to use\ \ your account normally again.\nAlso, please ensure that you've set this current\ \ account as the account you're moving from." From 3b9b6daba3d43ef3e80be5e3fd62ef69fad536b7 Mon Sep 17 00:00:00 2001 From: jolupa Date: Sat, 29 Apr 2023 11:53:12 +0000 Subject: [PATCH 0191/1248] chore: Translated using Weblate (Catalan) Currently translated at 37.1% (644 of 1735 strings) Translation: Calckey/locales Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ --- locales/ca-ES.yml | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 24e4b6d7d1..b226d641e4 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -675,3 +675,48 @@ useGlobalSetting: Fes servir els ajustos globals useGlobalSettingDesc: Si s'activa, es faran servir els ajustos de notificacions del teu compte. Si es desactiva , es poden fer configuracions individuals. other: Altres +menu: Menú +addItem: Afegeix un element +divider: Divisor +relays: Relés +addRelay: Afegeix un Relé +inboxUrl: Adreça de la safata d'entrada +addedRelays: Relés afegits +serviceworkerInfo: Ha de estar activat per les notificacions push. +poll: Enquesta +deletedNote: Article eliminat +disablePlayer: Tancar el reproductor de vídeo +fileIdOrUrl: ID o adreça URL del fitxer +behavior: Comportament +regenerateLoginTokenDescription: Regenera el token que es fa servir de manera interna + durant l'inici de sessió. Normalment això no és necessari. Si es torna a genera + el token, es tancarà la sessió a tots els dispositius. +setMultipleBySeparatingWithSpace: Separa diferents entrades amb espais. +reportAbuseOf: Informa sobre {name} +sample: Exemple +abuseReports: Informes +reportAbuse: Informe +reporter: Informador +reporterOrigin: Origen d'el informador +forwardReport: Envia l'informe a una instancia remota +abuseReported: El teu informe ha sigut enviat. Moltes gràcies. +reporteeOrigin: Origen de l'informe +send: Enviar +abuseMarkAsResolved: Marcar l'informe com a resolt +visibility: Visibilitat +useCw: Amaga el contingut +enablePlayer: Obre el reproductor de vídeo +yourAccountSuspendedDescription: Aquest compte ha sigut suspesa per no seguir els + termes de servei del servidor o quelcom similar. Contacte amb l'administrador si + vols conèixer la raó amb més detall. Si us plau no facis un compte nou. +invisibleNote: Article ocult +enableInfiniteScroll: Carregar més de forma automàtica +fillAbuseReportDescription: Si us plau omple els detalls sobre aquest informe. Si + es sobre un article en concret, si us plau inclou l'adreça URL. +forwardReportIsAnonymous: Com a informador a l'instància remota no es mostrarà el + teu compte, si no un compte anònim. +openInNewTab: Obrir en una pestanya nova +openInSideView: Obrir a la vista lateral +defaultNavigationBehaviour: Navegació per defecte +editTheseSettingsMayBreakAccount: Si edites aquestes configuracions pots fer mal bé + el teu compte. From 50d8301b2f01c611928f5a5e72448df61d2ed839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Lepist=C3=B6?= Date: Sat, 29 Apr 2023 08:52:45 +0000 Subject: [PATCH 0192/1248] chore: Translated using Weblate (Finnish) Currently translated at 11.7% (204 of 1735 strings) Translation: Calckey/locales Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ --- locales/fi.yml | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/locales/fi.yml b/locales/fi.yml index c9e5e06e08..558d1afc22 100644 --- a/locales/fi.yml +++ b/locales/fi.yml @@ -41,3 +41,182 @@ favorite: Lisää kirjanmerkkeihin copyContent: Kopioi sisältö deleteAndEdit: Poista ja muokkaa copyLink: Kopioi linkki +makeFollowManuallyApprove: Seuraajapyyntö vaatii hyväksymistä +follow: Seuraa +pinned: Kiinnitä profiiliin +followRequestPending: Seuraajapyyntö odottaa +you: Sinä +unrenote: Peruuta buustaus +reaction: Reaktiot +reactionSettingDescription2: Vedä uudelleenjärjestelläksesi, napsauta poistaaksesi, + paina "+" lisätäksesi. +attachCancel: Poista liite +enterFileName: Anna tiedostonimi +mute: Hiljennä +unmute: Poista hiljennys +headlineMisskey: Avoimen lähdekoodin, hajautettu sosiaalisen median alusta, joka on + ikuisesti ilmainen! 🚀 +monthAndDay: '{day}/{month}' +deleteAndEditConfirm: Oletko varma, että haluat poistaa tämän lähetyksen ja muokata + sitä? Menetät kaikki reaktiot, buustaukset ja vastaukset lähetyksestäsi. +addToList: Lisää listaan +sendMessage: Lähetä viesti +reply: Vastaa +loadMore: Lataa enemmän +showMore: Näytä enemmän +receiveFollowRequest: Seuraajapyyntö vastaanotettu +followRequestAccepted: Seuraajapyyntö hyväksytty +mentions: Maininnat +importAndExport: Tuo/Vie Tietosisältö +import: Tuo +export: Vie +files: Tiedostot +download: Lataa +unfollowConfirm: Oletko varma, ettet halua seurata enää käyttäjää {name}? +noLists: Sinulla ei ole listoja +note: Lähetys +notes: Lähetykset +following: Seuraa +createList: Luo lista +manageLists: Hallitse listoja +error: Virhe +somethingHappened: On tapahtunut virhe +retry: Yritä uudelleen +pageLoadError: Virhe ladattaessa sivua. +serverIsDead: Tämä palvelin ei vastaa. Yritä hetken kuluttua uudelleen. +youShouldUpgradeClient: Nähdäksesi tämän sivun, virkistä päivittääksesi asiakasohjelmasi. +privacy: Tietosuoja +defaultNoteVisibility: Oletusnäkyvyys +followRequest: Seuraajapyyntö +followRequests: Seuraajapyynnöt +unfollow: Poista seuraaminen +enterEmoji: Syötä emoji +renote: Buustaa +renoted: Buustattu. +cantRenote: Tätä lähetystä ei voi buustata. +cantReRenote: Buustausta ei voi buustata. +quote: Lainaus +pinnedNote: Lukittu lähetys +clickToShow: Napsauta nähdäksesi +sensitive: Herkkää sisältöä (NSFW) +add: Lisää +enableEmojiReactions: Ota käyttöön emoji-reaktiot +showEmojisInReactionNotifications: Näytä emojit reaktioilmoituksissa +reactionSetting: Reaktiot näytettäväksi reaktiovalitsimessa +rememberNoteVisibility: Muista lähetyksen näkyvyysasetukset +markAsSensitive: Merkitse herkäksi sisällöksi (NSFW) +unmarkAsSensitive: Poista merkintä herkkää sisältöä (NSFW) +renoteMute: Hiljennä buustit +renoteUnmute: Poista buustien hiljennys +block: Estä +unblock: Poista esto +unsuspend: Poista keskeytys +suspend: Keskeytys +blockConfirm: Oletko varma, että haluat estää tämän tilin? +unblockConfirm: Oletko varma, että haluat poistaa tämän tilin eston? +selectAntenna: Valitse antenni +selectWidget: Valitse vimpain +editWidgets: Muokkaa vimpaimia +editWidgetsExit: Valmis +emoji: Emoji +emojis: Emojit +emojiName: Emojin nimi +emojiUrl: Emojin URL-linkki +cacheRemoteFiles: Taltioi etätiedostot välimuistiin +flagAsBot: Merkitse tili botiksi +flagAsBotDescription: Ota tämä vaihtoehto käyttöön, jos tätä tiliä ohjaa ohjelma. + Jos se on käytössä, se toimii lippuna muille kehittäjille, jotta estetään loputtomat + vuorovaikutusketjut muiden bottien kanssa ja säädetään Calckeyn sisäiset järjestelmät + käsittelemään tätä tiliä botina. +flagAsCat: Oletko kissa? 🐱 +flagAsCatDescription: Saat kissan korvat ja puhut kuin kissa! +flagSpeakAsCat: Puhu kuin kissa +flagShowTimelineReplies: Näytä vastaukset aikajanalla +addAccount: Lisää tili +loginFailed: Kirjautuminen epäonnistui +showOnRemote: Katsele etäinstanssilla +general: Yleistä +accountMoved: 'Käyttäjä on muuttanut uuteen tiliin:' +wallpaper: Taustakuva +setWallpaper: Aseta taustakuva +searchWith: 'Etsi: {q}' +youHaveNoLists: Sinulla ei ole listoja +followConfirm: Oletko varma, että haluat seurata käyttäjää {name}? +host: Isäntä +selectUser: Valitse käyttäjä +annotation: Kommentit +registeredAt: Rekisteröity +latestRequestReceivedAt: Viimeisin pyyntö vastaanotettu +latestRequestSentAt: Viimeisin pyyntö lähetetty +storageUsage: Tallennustilan käyttö +charts: Kaaviot +stopActivityDelivery: Lopeta toimintojen lähettäminen +blockThisInstance: Estä tämä instanssi +operations: Toiminnot +metadata: Metatieto +monitor: Seuranta +jobQueue: Työjono +cpuAndMemory: Prosessori ja muisti +network: Verkko +disk: Levy +clearCachedFiles: Tyhjennä välimuisti +clearCachedFilesConfirm: Oletko varma, että haluat tyhjentää kaikki välimuistiin tallennetut + etätiedostot? +blockedInstances: Estetyt instanssit +hiddenTags: Piilotetut asiatunnisteet +mention: Maininta +copyUsername: Kopioi käyttäjänimi +searchUser: Etsi käyttäjää +showLess: Sulje +youGotNewFollower: seurasi sinua +directNotes: Yksityisviestit +driveFileDeleteConfirm: Oletko varma, että haluat poistaa tiedoston " {name}"? Lähetykset, + jotka sisältyvät tiedostoon, poistuvat myös. +importRequested: Olet pyytänyt viemistä. Tämä voi viedä hetken. +exportRequested: Olet pyytänyt tuomista. Tämä voi viedä hetken. Se lisätään asemaan + kun tuonti valmistuu. +lists: Listat +followers: Seuraajat +followsYou: Seuraa sinua +pageLoadErrorDescription: Tämä yleensä johtuu verkkovirheistä tai selaimen välimuistista. + Kokeile tyhjentämällä välimuisti ja yritä sitten hetken kuluttua uudelleen. +enterListName: Anna listalle nimi +withNFiles: '{n} tiedosto(t)' +instanceInfo: Instanssin tiedot +clearQueue: Tyhjennä jono +suspendConfirm: Oletko varma, että haluat keskeyttää tämän tilin? +unsuspendConfirm: Oletko varma, että haluat poistaa tämän tilin keskeytyksen? +selectList: Valitse lista +customEmojis: Kustomoitu Emoji +addEmoji: Lisää +settingGuide: Suositellut asetukset +cacheRemoteFilesDescription: Kun tämä asetus ei ole käytössä, etätiedostot on ladattu + suoraan etäinstanssilta. Asetuksen poistaminen käytöstä vähentää tallennustilan + käyttöä, mutta lisää verkkoliikennettä kun pienoiskuvat eivät muodostu. +flagSpeakAsCatDescription: Lähetyksesi nyanifioidaan, kun olet kissatilassa +flagShowTimelineRepliesDescription: Näyttää käyttäjien vastaukset muiden käyttäjien + lähetyksiin aikajanalla, jos se on päällä. +autoAcceptFollowed: Automaattisesti hyväksy seuraamispyynnöt käyttäjiltä, joita seuraat +perHour: Tunnissa +removeWallpaper: Poista taustakuva +recipient: Vastaanottaja(t) +federation: Federaatio +software: Ohjelmisto +proxyAccount: Proxy-tili +proxyAccountDescription: Välitystili (Proxy-tili) on tili, joka toimii käyttäjien + etäseuraajana tietyin edellytyksin. Kun käyttäjä esimerkiksi lisää etäkäyttäjän + luetteloon, etäkäyttäjän toimintaa ei toimiteta instanssiin, jos yksikään paikallinen + käyttäjä ei seuraa kyseistä käyttäjää, joten välitystili seuraa sen sijaan. +latestStatus: Viimeisin tila +selectInstance: Valitse instanssi +instances: Instanssit +perDay: Päivässä +version: Versio +statistics: Tilastot +clearQueueConfirmTitle: Oletko varma, että haluat tyhjentää jonon? +introMisskey: Tervetuloa! Calckey on avoimen lähdekoodin, hajautettu sosiaalisen median + alusta, joka on ikuisesti ilmainen! 🚀 +clearQueueConfirmText: Mitkään välittämättömät lähetykset, jotka ovat jonossa, eivät + federoidu. Yleensä tätä toimintoa ei tarvita. +blockedInstancesDescription: Lista instanssien isäntänimistä, jotka haluat estää. + Listatut instanssit eivät kykene kommunikoimaan enää tämän instanssin kanssa. From 7d25b7adc6c93979f1b38f876ae31a9c93791b71 Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Sat, 29 Apr 2023 08:11:18 +0000 Subject: [PATCH 0193/1248] chore: Translated using Weblate (Finnish) Currently translated at 11.7% (204 of 1735 strings) Translation: Calckey/locales Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ --- locales/fi.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/fi.yml b/locales/fi.yml index 558d1afc22..fd6c160e6c 100644 --- a/locales/fi.yml +++ b/locales/fi.yml @@ -3,7 +3,7 @@ fetchingAsApObject: Hae Fedeversestä gotIt: Selvä! cancel: Peruuta enterUsername: Anna käyttäjänimi -renotedBy: Buustannut {käyttäjä} +renotedBy: Buustannut {user} noNotes: Ei lähetyksiä noNotifications: Ei ilmoituksia instance: Instanssi @@ -220,3 +220,4 @@ clearQueueConfirmText: Mitkään välittämättömät lähetykset, jotka ovat jo federoidu. Yleensä tätä toimintoa ei tarvita. blockedInstancesDescription: Lista instanssien isäntänimistä, jotka haluat estää. Listatut instanssit eivät kykene kommunikoimaan enää tämän instanssin kanssa. +_lang_: Suomi From ba734a9f3c2070fbfbd35995e15b83d20a603d6c Mon Sep 17 00:00:00 2001 From: Namekuji Date: Sun, 30 Apr 2023 07:27:55 -0400 Subject: [PATCH 0194/1248] instance silence --- .../1682844825247-InstanceSilence.js | 63 +++++++++++++++++++ .../backend/src/misc/should-block-instance.ts | 17 +++++ packages/backend/src/models/entities/meta.ts | 5 ++ .../src/models/repositories/instance.ts | 5 +- .../src/models/schema/federation-instance.ts | 5 ++ .../src/server/api/endpoints/admin/meta.ts | 11 ++++ .../server/api/endpoints/admin/update-meta.ts | 16 +++++ .../api/endpoints/federation/instances.ts | 17 +++++ .../backend/src/services/following/create.ts | 7 ++- packages/backend/src/services/note/create.ts | 21 ++++++- packages/calckey-js/src/api.types.ts | 1 + .../client/src/pages/admin/instance-block.vue | 30 ++++++++- 12 files changed, 188 insertions(+), 10 deletions(-) create mode 100644 packages/backend/migration/1682844825247-InstanceSilence.js diff --git a/packages/backend/migration/1682844825247-InstanceSilence.js b/packages/backend/migration/1682844825247-InstanceSilence.js new file mode 100644 index 0000000000..5689c4f160 --- /dev/null +++ b/packages/backend/migration/1682844825247-InstanceSilence.js @@ -0,0 +1,63 @@ +export class InstanceSilence1682844825247 { + name = 'InstanceSilence1682844825247' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "fk_7f4e851a35d81b64dda28eee0"`); + await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_createdAt"`); + await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muteeId"`); + await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muterId"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "useStarForReactionFallback"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGuestTimeline"`); + await queryRunner.query(`ALTER TABLE "meta" ADD "silencedHosts" character varying(256) array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS 'Whether the notification was read.'`); + await queryRunner.query(`COMMENT ON COLUMN "meta"."defaultReaction" IS NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "secureMode" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "privateMode" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "allowedHosts" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-calckey}'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey/issues/new'`); + await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS 'The created date of the Muting.'`); + await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS 'The mutee user ID.'`); + await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS 'The muter user ID.'`); + await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "isPublic" DROP DEFAULT`); + await queryRunner.query(`CREATE INDEX "IDX_d1259a2c2b7bb413ff449e8711" ON "renote_muting" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_7eac97594bcac5ffcf2068089b" ON "renote_muting" ("muteeId") `); + await queryRunner.query(`CREATE INDEX "IDX_7aa72a5fe76019bfe8e5e0e8b7" ON "renote_muting" ("muterId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0d801c609cec4e9eb4b6b4490c" ON "renote_muting" ("muterId", "muteeId") `); + await queryRunner.query(`CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId") `); + await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`); + await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d"`); + await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6"`); + await queryRunner.query(`DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`); + await queryRunner.query(`DROP INDEX "public"."IDX_0d801c609cec4e9eb4b6b4490c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_7aa72a5fe76019bfe8e5e0e8b7"`); + await queryRunner.query(`DROP INDEX "public"."IDX_7eac97594bcac5ffcf2068089b"`); + await queryRunner.query(`DROP INDEX "public"."IDX_d1259a2c2b7bb413ff449e8711"`); + await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "isPublic" SET DEFAULT true`); + await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey/issues/new'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-misskey}'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "allowedHosts" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "privateMode" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "secureMode" DROP NOT NULL`); + await queryRunner.query(`COMMENT ON COLUMN "meta"."defaultReaction" IS 'The fallback reaction for emoji reacts'`); + await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS 'Whether the Notification is read.'`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`); + await queryRunner.query(`ALTER TABLE "meta" ADD "enableGuestTimeline" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "useStarForReactionFallback" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `); + await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `); + await queryRunner.query(`CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `); + await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "fk_7f4e851a35d81b64dda28eee0" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } +} diff --git a/packages/backend/src/misc/should-block-instance.ts b/packages/backend/src/misc/should-block-instance.ts index 6e46232428..66b5832c70 100644 --- a/packages/backend/src/misc/should-block-instance.ts +++ b/packages/backend/src/misc/should-block-instance.ts @@ -18,3 +18,20 @@ export async function shouldBlockInstance( (blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`), ); } + +/** + * Returns whether a specific host (punycoded) should be limited. + * + * @param host punycoded instance host + * @param meta a resolved Meta table + * @returns whether the given host should be limited + */ +export async function shouldSilenceInstance( + host: Instance["host"], + meta?: Meta, +): Promise { + const { silencedHosts } = meta ?? (await fetchMeta()); + return silencedHosts.some( + (limitedHost) => host === limitedHost || host.endsWith(`.${limitedHost}`), + ); +} diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts index 2f77796c4b..84f9af4793 100644 --- a/packages/backend/src/models/entities/meta.ts +++ b/packages/backend/src/models/entities/meta.ts @@ -97,6 +97,11 @@ export class Meta { }) public blockedHosts: string[]; + @Column('varchar', { + length: 256, array: true, default: '{}', + }) + public silencedHosts: string[]; + @Column('boolean', { default: false, }) diff --git a/packages/backend/src/models/repositories/instance.ts b/packages/backend/src/models/repositories/instance.ts index fb4498911a..bae32b5718 100644 --- a/packages/backend/src/models/repositories/instance.ts +++ b/packages/backend/src/models/repositories/instance.ts @@ -1,12 +1,10 @@ import { db } from "@/db/postgre.js"; import { Instance } from "@/models/entities/instance.js"; import type { Packed } from "@/misc/schema.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; -import { shouldBlockInstance } from "@/misc/should-block-instance.js"; +import { shouldBlockInstance, shouldSilenceInstance } from "@/misc/should-block-instance.js"; export const InstanceRepository = db.getRepository(Instance).extend({ async pack(instance: Instance): Promise> { - const meta = await fetchMeta(); return { id: instance.id, caughtAt: instance.caughtAt.toISOString(), @@ -22,6 +20,7 @@ export const InstanceRepository = db.getRepository(Instance).extend({ isNotResponding: instance.isNotResponding, isSuspended: instance.isSuspended, isBlocked: await shouldBlockInstance(instance.host), + isSilenced: await shouldSilenceInstance(instance.host), softwareName: instance.softwareName, softwareVersion: instance.softwareVersion, openRegistrations: instance.openRegistrations, diff --git a/packages/backend/src/models/schema/federation-instance.ts b/packages/backend/src/models/schema/federation-instance.ts index ed3369bf11..f793d40f62 100644 --- a/packages/backend/src/models/schema/federation-instance.ts +++ b/packages/backend/src/models/schema/federation-instance.ts @@ -68,6 +68,11 @@ export const packedFederationInstanceSchema = { optional: false, nullable: false, }, + isSilenced: { + type: "boolean", + optional: false, + nullable: false, + }, softwareName: { type: "string", optional: false, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index f0ac57892d..89928af11c 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -259,6 +259,16 @@ export const meta = { nullable: false, }, }, + silencedHosts: { + type: "array", + optional: true, + nullable: false, + items: { + type: "string", + optional: false, + nullable: false, + }, + }, allowedHosts: { type: "array", optional: true, @@ -524,6 +534,7 @@ export default define(meta, paramDef, async (ps, me) => { customSplashIcons: instance.customSplashIcons, hiddenTags: instance.hiddenTags, blockedHosts: instance.blockedHosts, + silencedHosts: instance.silencedHosts, allowedHosts: instance.allowedHosts, privateMode: instance.privateMode, secureMode: instance.secureMode, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index a230007323..7f92e5e29e 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -61,6 +61,13 @@ export const paramDef = { type: "string", }, }, + silencedHosts: { + type: "array", + nullable: true, + items: { + type: "string", + }, + }, allowedHosts: { type: "array", nullable: true, @@ -219,6 +226,15 @@ export default define(meta, paramDef, async (ps, me) => { }); } + if (Array.isArray(ps.silencedHosts)) { + let lastValue = ""; + set.silencedHosts = ps.silencedHosts.sort().filter((h) => { + const lv = lastValue; + lastValue = h; + return h !== "" && h !== lv; + }); + } + if (ps.themeColor !== undefined) { set.themeColor = ps.themeColor; } diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 8f6184b196..646f38282b 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -34,6 +34,7 @@ export const paramDef = { notResponding: { type: "boolean", nullable: true }, suspended: { type: "boolean", nullable: true }, federating: { type: "boolean", nullable: true }, + silenced: { type: "boolean", nullable: true }, subscribing: { type: "boolean", nullable: true }, publishing: { type: "boolean", nullable: true }, limit: { type: "integer", minimum: 1, maximum: 100, default: 30 }, @@ -115,6 +116,22 @@ export default define(meta, paramDef, async (ps, me) => { } } + if (typeof ps.silenced === "boolean") { + const meta = await fetchMeta(true); + if (ps.silenced) { + if (meta.silencedHosts.length === 0) { + return []; + } + query.andWhere("instance.host IN (:...silences)", { + silences: meta.silencedHosts, + }); + } else if (meta.silencedHosts.length > 0) { + query.andWhere("instance.host NOT IN (:...silences)", { + silences: meta.silencedHosts, + }); + } + } + if (typeof ps.notResponding === "boolean") { if (ps.notResponding) { query.andWhere("instance.isNotResponding = TRUE"); diff --git a/packages/backend/src/services/following/create.ts b/packages/backend/src/services/following/create.ts index 61a8c6b268..cb5a888beb 100644 --- a/packages/backend/src/services/following/create.ts +++ b/packages/backend/src/services/following/create.ts @@ -27,6 +27,7 @@ import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js import type { Packed } from "@/misc/schema.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { webhookDeliver } from "@/queue/index.js"; +import { shouldSilenceInstance } from "@/misc/should-block-instance.js"; const logger = new Logger("following/create"); @@ -227,12 +228,14 @@ export default async function ( // フォロー対象が鍵アカウントである or // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or - // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである + // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである or + // The follower is remote, the followee is local, and the follower is in a silenced instance. // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく if ( followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || - (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) + (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) || + (Users.isRemoteUser(follower) && Users.isLocalUser(followee) && await shouldSilenceInstance(follower.host)) ) { let autoAccept = false; diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 5dd324d89a..3bccf33f7f 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -39,7 +39,7 @@ import { } from "@/models/index.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import type { App } from "@/models/entities/app.js"; -import { Not, In } from "typeorm"; +import { Not, In, IsNull } from "typeorm"; import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js"; import { genId } from "@/misc/gen-id.js"; import { @@ -66,6 +66,7 @@ import { Cache } from "@/misc/cache.js"; import type { UserProfile } from "@/models/entities/user-profile.js"; import { db } from "@/db/postgre.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js"; +import { shouldSilenceInstance } from "@/misc/should-block-instance.js"; const mutedWordsCache = new Cache< { userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[] @@ -166,7 +167,8 @@ export default async ( data: Option, silent = false, ) => - new Promise(async (res, rej) => { +// rome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME +new Promise(async (res, rej) => { // If you reply outside the channel, match the scope of the target. // TODO (I think it's a process that could be done on the client side, but it's server side for now.) if ( @@ -203,6 +205,13 @@ export default async ( data.visibility = "home"; } + const inSilencedInstance = Users.isRemoteUser(user) && await shouldSilenceInstance(user.host); + + // If the + if (data.visibility === "public" && inSilencedInstance) { + data.visibility = "home"; + } + // Reject if the target of the renote is a public range other than "Home or Entire". if ( data.renote && @@ -307,6 +316,14 @@ export default async ( } } + // Remove from mention the local users who aren't following the remote user in the silenced instance. + if (inSilencedInstance) { + const relations = await Followings.findBy([ + { followeeId: user.id, followerHost: IsNull() }, // a local user following the silenced user + ]).then(rels => rels.map(rel => rel.followerId)); + mentionedUsers = mentionedUsers.filter(mentioned => relations.includes(mentioned.id)); + } + const note = await insertNote(user, data, tags, emojis, mentionedUsers); res(note); diff --git a/packages/calckey-js/src/api.types.ts b/packages/calckey-js/src/api.types.ts index bef00da4ea..478b86721c 100644 --- a/packages/calckey-js/src/api.types.ts +++ b/packages/calckey-js/src/api.types.ts @@ -55,6 +55,7 @@ export type Endpoints = { "admin/get-table-stats": { req: TODO; res: TODO }; "admin/invite": { req: TODO; res: TODO }; "admin/logs": { req: TODO; res: TODO }; + "admin/meta": { req: TODO; res: TODO }; "admin/reset-password": { req: TODO; res: TODO }; "admin/resolve-abuse-user-report": { req: TODO; res: TODO }; "admin/resync-chart": { req: TODO; res: TODO }; diff --git a/packages/client/src/pages/admin/instance-block.vue b/packages/client/src/pages/admin/instance-block.vue index 80231b11e2..688578ff45 100644 --- a/packages/client/src/pages/admin/instance-block.vue +++ b/packages/client/src/pages/admin/instance-block.vue @@ -2,18 +2,25 @@ - + {{ i18n.ts.blockedInstances }} + + {{ i18n.ts.silencedInstances }} + + @@ -35,15 +42,21 @@ import { i18n } from "@/i18n"; import { definePageMetadata } from "@/scripts/page-metadata"; let blockedHosts: string = $ref(""); +let silencedHosts: string = $ref(""); +let tab = $ref("block"); async function init() { const meta = await os.api("admin/meta"); - blockedHosts = meta.blockedHosts.join("\n"); + if (meta) { + blockedHosts = meta.blockedHosts.join("\n"); + silencedHosts = meta.silencedHosts.join("\n"); + } } function save() { os.apiWithDialog("admin/update-meta", { blockedHosts: blockedHosts.split("\n").map((h) => h.trim()) || [], + silencedHosts: silencedHosts.split("\n").map((h) => h.trim()) || [], }).then(() => { fetchInstance(); }); @@ -51,7 +64,18 @@ function save() { const headerActions = $computed(() => []); -const headerTabs = $computed(() => []); +const headerTabs = $computed(() => [ + { + key: "block", + title: i18n.ts.block, + icon: "ph-prohibit ph-bold ph-lg", + }, + { + key: "silence", + title: i18n.ts.silence, + icon: "ph-eye-slash ph-bold ph-lg", + }, +]); definePageMetadata({ title: i18n.ts.instanceBlocking, From 151b30c53d77d37db25d70eb025d3b7139be7d6d Mon Sep 17 00:00:00 2001 From: Namekuji Date: Sun, 30 Apr 2023 07:58:03 -0400 Subject: [PATCH 0195/1248] rename and comment --- packages/backend/src/misc/should-block-instance.ts | 2 +- packages/backend/src/services/note/create.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/misc/should-block-instance.ts b/packages/backend/src/misc/should-block-instance.ts index 66b5832c70..47f9200d49 100644 --- a/packages/backend/src/misc/should-block-instance.ts +++ b/packages/backend/src/misc/should-block-instance.ts @@ -32,6 +32,6 @@ export async function shouldSilenceInstance( ): Promise { const { silencedHosts } = meta ?? (await fetchMeta()); return silencedHosts.some( - (limitedHost) => host === limitedHost || host.endsWith(`.${limitedHost}`), + (silencedHost) => host === silencedHost || host.endsWith(`.${silencedHost}`), ); } diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 3bccf33f7f..bd3a0224a1 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -207,7 +207,7 @@ new Promise(async (res, rej) => { const inSilencedInstance = Users.isRemoteUser(user) && await shouldSilenceInstance(user.host); - // If the + // Enforce home visibility if the user is in a silenced instance. if (data.visibility === "public" && inSilencedInstance) { data.visibility = "home"; } From 9bc872dc00fbd026f3e1c88e5bfc697ac9be7c76 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Sun, 30 Apr 2023 08:08:45 -0400 Subject: [PATCH 0196/1248] format --- .../1682844825247-InstanceSilence.js | 220 +++++++++++++----- .../backend/src/misc/should-block-instance.ts | 3 +- .../src/models/repositories/instance.ts | 5 +- .../backend/src/services/following/create.ts | 4 +- packages/backend/src/services/note/create.ts | 13 +- .../client/src/pages/admin/instance-block.vue | 12 +- 6 files changed, 188 insertions(+), 69 deletions(-) diff --git a/packages/backend/migration/1682844825247-InstanceSilence.js b/packages/backend/migration/1682844825247-InstanceSilence.js index 5689c4f160..4c05b349d7 100644 --- a/packages/backend/migration/1682844825247-InstanceSilence.js +++ b/packages/backend/migration/1682844825247-InstanceSilence.js @@ -1,63 +1,165 @@ export class InstanceSilence1682844825247 { - name = 'InstanceSilence1682844825247' + name = "InstanceSilence1682844825247"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "fk_7f4e851a35d81b64dda28eee0"`); - await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_createdAt"`); - await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muteeId"`); - await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muterId"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "useStarForReactionFallback"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGuestTimeline"`); - await queryRunner.query(`ALTER TABLE "meta" ADD "silencedHosts" character varying(256) array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS 'Whether the notification was read.'`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."defaultReaction" IS NULL`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "secureMode" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "privateMode" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "allowedHosts" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-calckey}'`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey'`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey/issues/new'`); - await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS 'The created date of the Muting.'`); - await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS 'The mutee user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS 'The muter user ID.'`); - await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "isPublic" DROP DEFAULT`); - await queryRunner.query(`CREATE INDEX "IDX_d1259a2c2b7bb413ff449e8711" ON "renote_muting" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_7eac97594bcac5ffcf2068089b" ON "renote_muting" ("muteeId") `); - await queryRunner.query(`CREATE INDEX "IDX_7aa72a5fe76019bfe8e5e0e8b7" ON "renote_muting" ("muterId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0d801c609cec4e9eb4b6b4490c" ON "renote_muting" ("muterId", "muteeId") `); - await queryRunner.query(`CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId") `); - await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP CONSTRAINT "fk_7f4e851a35d81b64dda28eee0"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_renote_muting_createdAt"`, + ); + await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muteeId"`); + await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muterId"`); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "useStarForReactionFallback"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableGuestTimeline"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "silencedHosts" character varying(256) array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."isRead" IS 'Whether the notification was read.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."defaultReaction" IS NULL`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "secureMode" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "privateMode" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "allowedHosts" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-calckey}'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey/issues/new'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "renote_muting"."createdAt" IS 'The created date of the Muting.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "renote_muting"."muteeId" IS 'The mutee user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "renote_muting"."muterId" IS 'The muter user ID.'`, + ); + await queryRunner.query( + `ALTER TABLE "page" ALTER COLUMN "isPublic" DROP DEFAULT`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d1259a2c2b7bb413ff449e8711" ON "renote_muting" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7eac97594bcac5ffcf2068089b" ON "renote_muting" ("muteeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7aa72a5fe76019bfe8e5e0e8b7" ON "renote_muting" ("muterId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0d801c609cec4e9eb4b6b4490c" ON "renote_muting" ("muterId", "muteeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId") `, + ); + await queryRunner.query( + `ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`); - await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d"`); - await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6"`); - await queryRunner.query(`DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`); - await queryRunner.query(`DROP INDEX "public"."IDX_0d801c609cec4e9eb4b6b4490c"`); - await queryRunner.query(`DROP INDEX "public"."IDX_7aa72a5fe76019bfe8e5e0e8b7"`); - await queryRunner.query(`DROP INDEX "public"."IDX_7eac97594bcac5ffcf2068089b"`); - await queryRunner.query(`DROP INDEX "public"."IDX_d1259a2c2b7bb413ff449e8711"`); - await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "isPublic" SET DEFAULT true`); - await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS NULL`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey/issues/new'`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey'`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-misskey}'`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "allowedHosts" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "privateMode" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "secureMode" DROP NOT NULL`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."defaultReaction" IS 'The fallback reaction for emoji reacts'`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS 'Whether the Notification is read.'`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`); - await queryRunner.query(`ALTER TABLE "meta" ADD "enableGuestTimeline" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "meta" ADD "useStarForReactionFallback" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `); - await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `); - await queryRunner.query(`CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "fk_7f4e851a35d81b64dda28eee0" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`, + ); + await queryRunner.query( + `ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d"`, + ); + await queryRunner.query( + `ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_0d801c609cec4e9eb4b6b4490c"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_7aa72a5fe76019bfe8e5e0e8b7"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_7eac97594bcac5ffcf2068089b"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_d1259a2c2b7bb413ff449e8711"`, + ); + await queryRunner.query( + `ALTER TABLE "page" ALTER COLUMN "isPublic" SET DEFAULT true`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "renote_muting"."muterId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "renote_muting"."muteeId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "renote_muting"."createdAt" IS NULL`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey/issues/new'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-misskey}'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "allowedHosts" DROP NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "privateMode" DROP NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "secureMode" DROP NOT NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."defaultReaction" IS 'The fallback reaction for emoji reacts'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."isRead" IS 'Whether the Notification is read.'`, + ); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`); + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableGuestTimeline" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "useStarForReactionFallback" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD CONSTRAINT "fk_7f4e851a35d81b64dda28eee0" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } } diff --git a/packages/backend/src/misc/should-block-instance.ts b/packages/backend/src/misc/should-block-instance.ts index 47f9200d49..35ed307931 100644 --- a/packages/backend/src/misc/should-block-instance.ts +++ b/packages/backend/src/misc/should-block-instance.ts @@ -32,6 +32,7 @@ export async function shouldSilenceInstance( ): Promise { const { silencedHosts } = meta ?? (await fetchMeta()); return silencedHosts.some( - (silencedHost) => host === silencedHost || host.endsWith(`.${silencedHost}`), + (silencedHost) => + host === silencedHost || host.endsWith(`.${silencedHost}`), ); } diff --git a/packages/backend/src/models/repositories/instance.ts b/packages/backend/src/models/repositories/instance.ts index bae32b5718..667ec948de 100644 --- a/packages/backend/src/models/repositories/instance.ts +++ b/packages/backend/src/models/repositories/instance.ts @@ -1,7 +1,10 @@ import { db } from "@/db/postgre.js"; import { Instance } from "@/models/entities/instance.js"; import type { Packed } from "@/misc/schema.js"; -import { shouldBlockInstance, shouldSilenceInstance } from "@/misc/should-block-instance.js"; +import { + shouldBlockInstance, + shouldSilenceInstance, +} from "@/misc/should-block-instance.js"; export const InstanceRepository = db.getRepository(Instance).extend({ async pack(instance: Instance): Promise> { diff --git a/packages/backend/src/services/following/create.ts b/packages/backend/src/services/following/create.ts index cb5a888beb..c987a01e59 100644 --- a/packages/backend/src/services/following/create.ts +++ b/packages/backend/src/services/following/create.ts @@ -235,7 +235,9 @@ export default async function ( followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) || - (Users.isRemoteUser(follower) && Users.isLocalUser(followee) && await shouldSilenceInstance(follower.host)) + (Users.isRemoteUser(follower) && + Users.isLocalUser(followee) && + (await shouldSilenceInstance(follower.host))) ) { let autoAccept = false; diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index bd3a0224a1..ad50bd97a4 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -167,8 +167,8 @@ export default async ( data: Option, silent = false, ) => -// rome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME -new Promise(async (res, rej) => { + // rome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME + new Promise(async (res, rej) => { // If you reply outside the channel, match the scope of the target. // TODO (I think it's a process that could be done on the client side, but it's server side for now.) if ( @@ -205,7 +205,8 @@ new Promise(async (res, rej) => { data.visibility = "home"; } - const inSilencedInstance = Users.isRemoteUser(user) && await shouldSilenceInstance(user.host); + const inSilencedInstance = + Users.isRemoteUser(user) && (await shouldSilenceInstance(user.host)); // Enforce home visibility if the user is in a silenced instance. if (data.visibility === "public" && inSilencedInstance) { @@ -320,8 +321,10 @@ new Promise(async (res, rej) => { if (inSilencedInstance) { const relations = await Followings.findBy([ { followeeId: user.id, followerHost: IsNull() }, // a local user following the silenced user - ]).then(rels => rels.map(rel => rel.followerId)); - mentionedUsers = mentionedUsers.filter(mentioned => relations.includes(mentioned.id)); + ]).then((rels) => rels.map((rel) => rel.followerId)); + mentionedUsers = mentionedUsers.filter((mentioned) => + relations.includes(mentioned.id), + ); } const note = await insertNote(user, data, tags, emojis, mentionedUsers); diff --git a/packages/client/src/pages/admin/instance-block.vue b/packages/client/src/pages/admin/instance-block.vue index 688578ff45..3a9a17fa08 100644 --- a/packages/client/src/pages/admin/instance-block.vue +++ b/packages/client/src/pages/admin/instance-block.vue @@ -9,13 +9,21 @@ /> - + {{ i18n.ts.blockedInstances }} - + {{ i18n.ts.silencedInstances }}