mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-26 10:23:09 +02:00
Compare commits
No commits in common. "2e7df3297c13826c4c37f1c06bd87bc7cfba9e4c" and "af5ebdfcedd763c236ea3718f06827040d24c458" have entirely different histories.
2e7df3297c
...
af5ebdfced
91 changed files with 487 additions and 1075 deletions
|
@ -24,8 +24,6 @@
|
||||||
- Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正
|
- Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正
|
||||||
* すべてのリモートユーザーのリアクション一覧を見えないようにします
|
* すべてのリモートユーザーのリアクション一覧を見えないようにします
|
||||||
- Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように
|
- Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように
|
||||||
- Fix: 特定のキーワードを含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207
|
|
||||||
* デフォルトは空欄なので適用前と同等の動作になります
|
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Feat: 新しいゲームを追加
|
- Feat: 新しいゲームを追加
|
||||||
|
@ -76,7 +74,6 @@
|
||||||
- Fix: プロフィールを編集してもリロードするまで反映されない問題を修正
|
- Fix: プロフィールを編集してもリロードするまで反映されない問題を修正
|
||||||
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
||||||
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
|
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
|
||||||
- Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
|
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
|
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
|
||||||
|
|
|
@ -286,17 +286,18 @@ export const argTypes = {
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 4,
|
max: 4,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Also, you can use msw to mock API requests in the storybook. Creating a `MyComponent.stories.msw.ts` file to define the mock handlers.
|
Also, you can use msw to mock API requests in the storybook. Creating a `MyComponent.stories.msw.ts` file to define the mock handlers.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
export const handlers = [
|
export const handlers = [
|
||||||
http.post('/api/notes/timeline', ({ request }) => {
|
rest.post('/api/notes/timeline', (req, res, ctx) => {
|
||||||
return HttpResponse.json([]);
|
return res(
|
||||||
|
ctx.json([]),
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
|
@ -1041,9 +1041,6 @@ resetPasswordConfirm: "Vols canviar la teva contrasenya?"
|
||||||
sensitiveWords: "Paraules sensibles"
|
sensitiveWords: "Paraules sensibles"
|
||||||
sensitiveWordsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automàticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per línies noves."
|
sensitiveWordsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automàticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per línies noves."
|
||||||
sensitiveWordsDescription2: "Fent servir espais crearà expressions AND si l'expressió s'envolta amb barres inclinades es converteix en una expressió regular."
|
sensitiveWordsDescription2: "Fent servir espais crearà expressions AND si l'expressió s'envolta amb barres inclinades es converteix en una expressió regular."
|
||||||
prohibitedWords: "Paraules prohibides"
|
|
||||||
prohibitedWordsDescription: "Quan intenteu publicar una Nota que conté una paraula prohibida, feu que es converteixi en un error. Es poden dividir i establir múltiples línies."
|
|
||||||
prohibitedWordsDescription2: "Fent servir espais crearà expressions AND si l'expressió s'envolta amb barres inclinades es converteix en una expressió regular."
|
|
||||||
hiddenTags: "Etiquetes ocultes"
|
hiddenTags: "Etiquetes ocultes"
|
||||||
hiddenTagsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automàticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per línies noves."
|
hiddenTagsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automàticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per línies noves."
|
||||||
notesSearchNotAvailable: "La cerca de notes no es troba disponible."
|
notesSearchNotAvailable: "La cerca de notes no es troba disponible."
|
||||||
|
@ -1521,82 +1518,12 @@ _achievements:
|
||||||
title: "Nocturn"
|
title: "Nocturn"
|
||||||
description: "Publica una nota a altes hores de la nit "
|
description: "Publica una nota a altes hores de la nit "
|
||||||
flavor: "És hora d'anar a dormir."
|
flavor: "És hora d'anar a dormir."
|
||||||
_postedAt0min0sec:
|
|
||||||
title: "Rellotge xerraire"
|
|
||||||
description: "Publica una nota a les 0:00"
|
|
||||||
flavor: "Tic tac, tic tac, tic tac, DING!"
|
|
||||||
_selfQuote:
|
|
||||||
title: "Autoreferència "
|
|
||||||
description: "Cita una nota teva"
|
|
||||||
_htl20npm:
|
|
||||||
title: "Línia de temps fluida"
|
|
||||||
description: "La teva línia de temps va a més de 20npm (notes per minut)"
|
|
||||||
_viewInstanceChart:
|
|
||||||
title: "Analista "
|
|
||||||
description: "Mira els gràfics de la teva instància "
|
|
||||||
_outputHelloWorldOnScratchpad:
|
|
||||||
title: "Hola, món!"
|
|
||||||
description: "Escriu \"hola, món\" al bloc de notes"
|
|
||||||
_open3windows:
|
_open3windows:
|
||||||
title: "Multi finestres"
|
title: "Multi finestres"
|
||||||
description: "I va obrir més de tres finestres"
|
description: "I va obrir més de tres finestres"
|
||||||
_driveFolderCircularReference:
|
_driveFolderCircularReference:
|
||||||
title: "Consulteu la secció de bucle"
|
title: "Consulteu la secció de bucle"
|
||||||
description: "Intenta crear carpetes recursives al Disc"
|
|
||||||
_reactWithoutRead:
|
|
||||||
title: "De veritat has llegit això?"
|
|
||||||
description: "Reaccions a una nota de més de 100 caràcters publicada fa menys de 3 segons "
|
|
||||||
_clickedClickHere:
|
|
||||||
title: "Fer clic"
|
|
||||||
description: "Has fet clic aquí "
|
|
||||||
_justPlainLucky:
|
|
||||||
title: "Ha sigut sort"
|
|
||||||
description: "Oportunitat de guanyar-lo amb una probabilitat d'un 0.005% cada 10 segons"
|
|
||||||
_setNameToSyuilo:
|
|
||||||
title: "soc millor"
|
|
||||||
description: "Posat \"siuylo\" com a nom"
|
|
||||||
_passedSinceAccountCreated1:
|
|
||||||
title: "Primer aniversari"
|
|
||||||
description: "Ja ha passat un any d'ençà que vas crear el teu compte"
|
|
||||||
_passedSinceAccountCreated2:
|
|
||||||
title: "Segon aniversari"
|
|
||||||
description: "Ja han passat dos anys d'ençà que vas crear el teu compte"
|
|
||||||
_passedSinceAccountCreated3:
|
|
||||||
title: "Tres anys"
|
|
||||||
description: "Ja han passat tres anys d'ençà que vas crear el teu compte"
|
|
||||||
_loggedInOnBirthday:
|
|
||||||
title: "Felicitats!"
|
|
||||||
description: "T'has identificat el dia del teu aniversari"
|
|
||||||
_loggedInOnNewYearsDay:
|
|
||||||
title: "Bon any nou!"
|
|
||||||
description: "T'has identificat el primer dia de l'any "
|
|
||||||
flavor: "A per un altre any memorable a la teva instància "
|
|
||||||
_cookieClicked:
|
|
||||||
title: "Un joc en què fas clic a les galetes"
|
|
||||||
description: "Pica galetes"
|
|
||||||
flavor: "Espera, ets al lloc web correcte?"
|
|
||||||
_brainDiver:
|
|
||||||
title: "Busseja Ments"
|
|
||||||
description: "Publica un enllaç al Busseja Ments"
|
|
||||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
|
||||||
_smashTestNotificationButton:
|
|
||||||
title: "Sobrecàrrega de proves"
|
|
||||||
description: "Envia moltes notificacions de prova en un període de temps molt curt"
|
|
||||||
_tutorialCompleted:
|
|
||||||
title: "Diploma del Curs Elemental de Misskey"
|
|
||||||
description: "Has completat el tutorial"
|
|
||||||
_bubbleGameExplodingHead:
|
|
||||||
title: "🤯"
|
|
||||||
description: "L'objecte més gran del joc de la bombolla "
|
|
||||||
_bubbleGameDoubleExplodingHead:
|
|
||||||
title: "Doble 🤯"
|
|
||||||
description: "Dos dels objectes més grans del joc de la bombolla al mateix temps"
|
|
||||||
flavor: "Pots emplenar una carmanyola com aquesta 🤯🤯 una mica"
|
|
||||||
_role:
|
_role:
|
||||||
new: "Nou rol"
|
|
||||||
edit: "Editar el rol"
|
|
||||||
name: "Nom del rol"
|
|
||||||
description: "Descripció del rol"
|
|
||||||
permission: "Permisos de rol"
|
permission: "Permisos de rol"
|
||||||
descriptionOfPermission: "Els <b>Moderadors</b> poden fer operacions bàsiques de moderació.\nEls <b>Administradors</b> poden canviar tots els ajustos del servidor."
|
descriptionOfPermission: "Els <b>Moderadors</b> poden fer operacions bàsiques de moderació.\nEls <b>Administradors</b> poden canviar tots els ajustos del servidor."
|
||||||
assignTarget: "Assignar "
|
assignTarget: "Assignar "
|
||||||
|
@ -1618,259 +1545,35 @@ _role:
|
||||||
asBadge: "Mostrar com a insígnia "
|
asBadge: "Mostrar com a insígnia "
|
||||||
descriptionOfAsBadge: "La icona d'aquest rol es mostrarà al costat dels noms d'usuaris que tinguin assignats aquest rol."
|
descriptionOfAsBadge: "La icona d'aquest rol es mostrarà al costat dels noms d'usuaris que tinguin assignats aquest rol."
|
||||||
isExplorable: "Fer el rol explorable"
|
isExplorable: "Fer el rol explorable"
|
||||||
descriptionOfIsExplorable: "La línia de temps d'aquest rol i la llista d'usuaris seran públics si s'activa."
|
|
||||||
displayOrder: "Posició "
|
|
||||||
descriptionOfDisplayOrder: "Com més gran és el número, més dalt la seva posició a la interfície."
|
|
||||||
canEditMembersByModerator: "Permetre que els moderadors editin la llista d'usuaris en aquest rol"
|
|
||||||
descriptionOfCanEditMembersByModerator: "Quan s'activa, els moderadors, així com els administradors, podran afegir i treure usuaris d'aquest rol. Si es troba desactivat, només els administradors poden assignar usuaris."
|
|
||||||
priority: "Prioritat"
|
priority: "Prioritat"
|
||||||
_priority:
|
_priority:
|
||||||
low: "Baixa"
|
low: "Baixa"
|
||||||
middle: "Mitjà"
|
middle: "Mitjà"
|
||||||
high: "Alta"
|
high: "Alta"
|
||||||
_options:
|
_options:
|
||||||
gtlAvailable: "Pot veure la línia de temps global"
|
|
||||||
ltlAvailable: "Pot veure la línia de temps local"
|
|
||||||
canPublicNote: "Pot enviar notes públiques"
|
|
||||||
canInvite: "Pot crear invitacions a la instància "
|
|
||||||
inviteLimit: "Límit d'invitacions "
|
|
||||||
inviteLimitCycle: "Temps de refresc de les invitacions"
|
|
||||||
inviteExpirationTime: "Interval de caducitat de les invitacions"
|
|
||||||
canManageCustomEmojis: "Gestiona els emojis personalitzats"
|
canManageCustomEmojis: "Gestiona els emojis personalitzats"
|
||||||
canManageAvatarDecorations: "Gestiona les decoracions dels avatars "
|
canManageAvatarDecorations: "Gestiona les decoracions dels avatars "
|
||||||
driveCapacity: "Capacitat del disc"
|
|
||||||
alwaysMarkNsfw: "Marca sempre els fitxers com a sensibles"
|
|
||||||
pinMax: "Nombre màxim de notes fixades"
|
|
||||||
antennaMax: "Nombre màxim d'antenes"
|
antennaMax: "Nombre màxim d'antenes"
|
||||||
wordMuteMax: "Nombre màxim de caràcters permesos a les paraules silenciades"
|
|
||||||
webhookMax: "Nombre màxim de Webhooks"
|
|
||||||
clipMax: "Nombre màxim de clips"
|
|
||||||
noteEachClipsMax: "Nombre màxim de notes dintre d'un clip"
|
|
||||||
userListMax: "Nombre màxim de llistes d'usuaris "
|
|
||||||
userEachUserListsMax: "Nombre màxim d'usuaris dintre d'una llista d'usuaris "
|
|
||||||
rateLimitFactor: "Limitador"
|
|
||||||
descriptionOfRateLimitFactor: "Límits baixos són menys restrictius, límits alts són més restrictius."
|
|
||||||
canHideAds: "Pot amagar els anuncis"
|
|
||||||
canSearchNotes: "Pot cercar notes"
|
|
||||||
canUseTranslator: "Pot fer servir el traductor"
|
|
||||||
avatarDecorationLimit: "Nombre màxim de decoracions que es poden aplicar els avatars"
|
|
||||||
_condition:
|
|
||||||
isLocal: "Usuari local"
|
|
||||||
isRemote: "Usuari remot"
|
|
||||||
createdLessThan: "Han passat menys de X a passat des de la creació del compte"
|
|
||||||
createdMoreThan: "Han passat més de X des de la creació del compte"
|
|
||||||
followersLessThanOrEq: "Té menys de X seguidors"
|
|
||||||
followersMoreThanOrEq: "Té X o més seguidors"
|
|
||||||
followingLessThanOrEq: "Segueix X o menys comptes"
|
|
||||||
followingMoreThanOrEq: "Segueix a X o més comptes"
|
|
||||||
notesLessThanOrEq: "Les publicacions són menys o igual a "
|
|
||||||
notesMoreThanOrEq: "Les publicacions són més o igual a "
|
|
||||||
and: "AND condicional "
|
|
||||||
or: "OR condicional"
|
|
||||||
not: "NOT condicional"
|
|
||||||
_sensitiveMediaDetection:
|
|
||||||
description: "Redueix els esforços de moderació gràcies al reconeixement automàtic dels fitxers amb contingut sensible mitjançant Machine Learing. Això augmentarà la càrrega del servidor."
|
|
||||||
sensitivity: "Sensibilitat de la detecció "
|
|
||||||
sensitivityDescription: "Reduint la sensibilitat provocarà menys falsos positius. D'altra banda incrementant-ho generarà més falsos negatius."
|
|
||||||
setSensitiveFlagAutomatically: "Marcar com a sensible"
|
|
||||||
setSensitiveFlagAutomaticallyDescription: "Els resultats de la detecció interna seran desats, inclòs si aquesta opció es troba desactivada."
|
|
||||||
analyzeVideos: "Activar anàlisis de vídeos "
|
|
||||||
analyzeVideosDescription: "Analitzar els vídeos a més de les imatges. Això incrementarà lleugerament la càrrega del servidor."
|
|
||||||
_emailUnavailable:
|
|
||||||
used: "Aquest correu electrònic ja s'està fent servir"
|
|
||||||
format: "El format del correu electrònic és invàlid "
|
|
||||||
disposable: "No es poden fer servir adreces de correu electrònic d'un sol ús "
|
|
||||||
mx: "Aquest servidor de correu electrònic no és vàlid "
|
|
||||||
smtp: "Aquest servidor de correu electrònic no respon"
|
|
||||||
banned: "No pots registrar-te amb aquesta adreça de correu electrònic "
|
|
||||||
_ffVisibility:
|
_ffVisibility:
|
||||||
public: "Publicar"
|
public: "Publicar"
|
||||||
followers: "Visible només per a seguidors "
|
|
||||||
private: "Privat"
|
|
||||||
_signup:
|
|
||||||
almostThere: "Ja quasi estem"
|
|
||||||
emailAddressInfo: "Si us plau, escriu la teva adreça de correu electrònic. No es farà pública."
|
|
||||||
emailSent: "S'ha enviat un correu de confirmació a ({email}). Si us plau, fes clic a l'enllaç per completar el registre."
|
|
||||||
_accountDelete:
|
|
||||||
accountDelete: "Eliminar el compte"
|
|
||||||
mayTakeTime: "Com l'eliminació d'un compte consumeix bastants recursos, pot trigar un temps perquè es completi l'esborrat, depenent si tens molt contingut i la quantitat de fitxer que hagis pujat."
|
|
||||||
sendEmail: "Una vegada hagi finalitzat l'esborrat del compte rebràs un correu electrònic a l'adreça que tinguis registrada en aquest compte."
|
|
||||||
requestAccountDelete: "Demanar l'eliminació del compte"
|
|
||||||
started: "Ha començat l'esborrat del compte."
|
|
||||||
inProgress: "L'esborrat es troba en procés "
|
|
||||||
_ad:
|
_ad:
|
||||||
back: "Tornar"
|
back: "Tornar"
|
||||||
reduceFrequencyOfThisAd: "Mostrar menys aquest anunci"
|
|
||||||
hide: "No mostrar mai"
|
|
||||||
timezoneinfo: "El dia de la setmana ve determinat del fus horari del servidor."
|
|
||||||
adsSettings: "Configuració d'anuncis "
|
|
||||||
notesPerOneAd: "Interval d'emplaçament d'anuncis en temps real (Notes per anuncis)"
|
|
||||||
setZeroToDisable: "Ajusta aquest valor a 0 per deshabilitar l'actualització d'anuncis en temps real"
|
|
||||||
adsTooClose: "L'interval actual pot fer que l'experiència de l'usuari sigui dolenta perquè l'interval és molt baix."
|
|
||||||
_forgotPassword:
|
|
||||||
enterEmail: "Escriu l'adreça de correu electrònic amb la que et vas registrar. S'enviarà un correu electrònic amb un enllaç perquè puguis canviar-la."
|
|
||||||
ifNoEmail: "Si no vas fer servir una adreça de correu electrònic per registrar-te, si us plau posa't en contacte amb l'administrador."
|
|
||||||
contactAdmin: "Aquesta instància no suporta registrar-se amb correu electrònic. Si us plau, contacta amb l'administrador del servidor."
|
|
||||||
_gallery:
|
|
||||||
my: "La meva Galeria "
|
|
||||||
liked: "Publicacions que t'han agradat"
|
|
||||||
like: "M'agrada "
|
|
||||||
unlike: "Ja no m'agrada"
|
|
||||||
_email:
|
_email:
|
||||||
_follow:
|
_follow:
|
||||||
title: "t'ha seguit"
|
title: "t'ha seguit"
|
||||||
_receiveFollowRequest:
|
|
||||||
title: "Has rebut una sol·licitud de seguiment"
|
|
||||||
_plugin:
|
|
||||||
install: "Instal·lar un afegit "
|
|
||||||
installWarn: "Si us plau, no instal·lis afegits que no siguin de confiança."
|
|
||||||
manage: "Gestionar els afegits"
|
|
||||||
viewSource: "Veure l'origen "
|
|
||||||
_preferencesBackups:
|
|
||||||
list: "Llista de còpies de seguretat"
|
|
||||||
saveNew: "Fer una còpia de seguretat nova"
|
|
||||||
loadFile: "Carregar des d'un fitxer"
|
|
||||||
apply: "Aplicar en aquest dispositiu"
|
|
||||||
save: "Desar els canvis"
|
|
||||||
inputName: "Escriu un nom per aquesta còpia de seguretat"
|
|
||||||
cannotSave: "No s'ha pogut desar"
|
|
||||||
nameAlreadyExists: "Ja existeix una còpia de seguretat anomenada \"{name}\". Escriu un nom diferent."
|
|
||||||
applyConfirm: "Vols aplicar la còpia de seguretat \"{name}\" a aquest dispositiu? La configuració actual del dispositiu serà esborrada."
|
|
||||||
saveConfirm: "Desar còpia de seguretat com {name}?"
|
|
||||||
deleteConfirm: "Esborrar la còpia de seguretat {name}?"
|
|
||||||
renameConfirm: "Vols canvia el nom de la còpia de seguretat de \"{old}\" a \"{new}\"?"
|
|
||||||
noBackups: "No hi ha còpies de seguretat. Pots fer una còpia de seguretat de la configuració d'aquest dispositiu al servidor fent servir \"Crear nova còpia de seguretat\""
|
|
||||||
createdAt: "Creat el: {date} {time}"
|
|
||||||
updatedAt: "Actualitzat el: {date} {time}"
|
|
||||||
cannotLoad: "Hi ha hagut un error al carregar"
|
|
||||||
invalidFile: "Format del fitxer no vàlid "
|
|
||||||
_registry:
|
|
||||||
scope: "Àmbit "
|
|
||||||
key: "Clau"
|
|
||||||
keys: "Claus"
|
|
||||||
domain: "Domini"
|
|
||||||
createKey: "Crear una clau"
|
|
||||||
_aboutMisskey:
|
|
||||||
about: "Misskey és un programa de codi obert desenvolupar per syuilo des de 2014"
|
|
||||||
contributors: "Col·laboradors principals"
|
|
||||||
allContributors: "Tots els col·laboradors "
|
|
||||||
source: "Codi font"
|
|
||||||
translation: "Tradueix Misskey"
|
|
||||||
donate: "Fes un donatiu a Misskey"
|
|
||||||
morePatrons: "També agraïm el suport d'altres col·laboradors que no surten en aquesta llista. Gràcies! 🥰"
|
|
||||||
patrons: "Patrocinadors"
|
|
||||||
projectMembers: "Membres del projecte"
|
|
||||||
_displayOfSensitiveMedia:
|
|
||||||
respect: "Ocultar imatges o vídeos marcats com a sensibles"
|
|
||||||
ignore: "Mostrar imatges o vídeos marcats com a sensibles"
|
|
||||||
force: "Ocultar totes les imatges o vídeos "
|
|
||||||
_instanceTicker:
|
|
||||||
none: "No mostrar mai"
|
|
||||||
remote: "Mostrar per usuaris remots"
|
|
||||||
always: "Mostrar sempre"
|
|
||||||
_serverDisconnectedBehavior:
|
|
||||||
reload: "Recarregar automàticament "
|
|
||||||
dialog: "Mostrar finestres de confirmació "
|
|
||||||
quiet: "Mostrar un avís que no molesti"
|
|
||||||
_channel:
|
|
||||||
create: "Crear un canal"
|
|
||||||
edit: "Editar canal"
|
|
||||||
setBanner: "Estableix el bàner "
|
|
||||||
removeBanner: "Eliminar el.bàner"
|
|
||||||
featured: "Popular"
|
|
||||||
owned: "Propietat"
|
|
||||||
following: "Seguin"
|
|
||||||
usersCount: "{n} Participants"
|
|
||||||
notesCount: "{n} Notes"
|
|
||||||
nameAndDescription: "Nom i descripció "
|
|
||||||
nameOnly: "Nom només "
|
|
||||||
allowRenoteToExternal: "Permet la citació i l'impuls fora del canal"
|
|
||||||
_instanceMute:
|
_instanceMute:
|
||||||
instanceMuteDescription: "Silencia tots els impulsos dels servidors seleccionats, també els usuaris que responen a altres d'un servidor silenciat."
|
instanceMuteDescription: "Silencia tots els impulsos dels servidors seleccionats, també els usuaris que responen a altres d'un servidor silenciat."
|
||||||
_theme:
|
_theme:
|
||||||
description: "Descripció"
|
description: "Descripció"
|
||||||
keys:
|
keys:
|
||||||
navHoverFg: "Text barra lateral (en passar per sobre)"
|
|
||||||
navActive: "Text barra lateral (actiu)"
|
|
||||||
navIndicator: "Indicador barra lateral"
|
|
||||||
link: "Enllaç"
|
|
||||||
hashtag: "Etiqueta"
|
|
||||||
mention: "Menció"
|
mention: "Menció"
|
||||||
mentionMe: "Mencions (jo)"
|
|
||||||
renote: "Renotar"
|
renote: "Renotar"
|
||||||
modalBg: "Fons del modal"
|
|
||||||
divider: "Divisor"
|
divider: "Divisor"
|
||||||
scrollbarHandle: "Maneta de la barra de desplaçament"
|
|
||||||
scrollbarHandleHover: "Maneta de la barra de desplaçament (en passar-hi per sobre)"
|
|
||||||
dateLabelFg: "Text de l'etiqueta de la data"
|
|
||||||
infoBg: "Fons d'informació "
|
|
||||||
infoFg: "Text d'informació "
|
|
||||||
infoWarnBg: "Fons avís "
|
|
||||||
infoWarnFg: "Text avís "
|
|
||||||
toastBg: "Fons notificació "
|
|
||||||
toastFg: "Text notificació "
|
|
||||||
buttonBg: "Fons botó "
|
|
||||||
buttonHoverBg: "Fons botó (en passar-hi per sobre)"
|
|
||||||
inputBorder: "Contorn del cap d'introducció "
|
|
||||||
listItemHoverBg: "Fons dels elements d'una llista"
|
|
||||||
driveFolderBg: "Fons de la carpeta Disc"
|
|
||||||
wallpaperOverlay: "Superposició del fons de pantalla "
|
|
||||||
badge: "Insígnia "
|
|
||||||
messageBg: "Fons del xat"
|
|
||||||
accentDarken: "Accent (fosc)"
|
|
||||||
accentLighten: "Accent (clar)"
|
|
||||||
fgHighlighted: "Text ressaltat"
|
|
||||||
_sfx:
|
_sfx:
|
||||||
note: "Notes"
|
note: "Notes"
|
||||||
noteMy: "Nota (per mi)"
|
|
||||||
notification: "Notificacions"
|
notification: "Notificacions"
|
||||||
antenna: "Antenes"
|
antenna: "Antenes"
|
||||||
channel: "Notificacions dels canals"
|
|
||||||
reaction: "Quan se selecciona una reacció "
|
|
||||||
_soundSettings:
|
|
||||||
driveFile: "Fer servir un fitxer d'àudio del disc"
|
|
||||||
driveFileWarn: "Seleccionar un fitxer d'àudio del disc"
|
|
||||||
driveFileTypeWarn: "Fitxer no suportat "
|
|
||||||
driveFileTypeWarnDescription: "Seleccionar un fitxer d'àudio "
|
|
||||||
driveFileDurationWarn: "L'àudio és massa llarg"
|
|
||||||
driveFileDurationWarnDescription: "Els àudios molt llargs pot interrompre l'ús de Misskey. Vols continuar?"
|
|
||||||
_ago:
|
|
||||||
future: "Futur "
|
|
||||||
justNow: "Ara mateix"
|
|
||||||
secondsAgo: "Fa {n} segons"
|
|
||||||
minutesAgo: "Fa {n} minuts"
|
|
||||||
hoursAgo: "Fa {n} hores"
|
|
||||||
daysAgo: "Fa {n} dies"
|
|
||||||
weeksAgo: "Fa {n} setmanes"
|
|
||||||
monthsAgo: "Fa {n} mesos"
|
|
||||||
yearsAgo: "Fa {n} anys"
|
|
||||||
invalid: "Res"
|
|
||||||
_timeIn:
|
|
||||||
seconds: "En {n} segons"
|
|
||||||
minutes: "En {n} minuts"
|
|
||||||
hours: "En {n} hores"
|
|
||||||
days: "En {n} dies"
|
|
||||||
weeks: "En {n} setmanes"
|
|
||||||
months: "En {n} mesos"
|
|
||||||
years: "En {n} anys"
|
|
||||||
_time:
|
|
||||||
second: "Segon(s)"
|
|
||||||
minute: "Minut(s)"
|
|
||||||
hour: "Hor(a)(es)"
|
|
||||||
day: "Di(a)(es)"
|
|
||||||
_2fa:
|
_2fa:
|
||||||
alreadyRegistered: "J has registrat un dispositiu d'autenticació de doble factor."
|
|
||||||
registerTOTP: "Registrar una aplicació autenticadora"
|
|
||||||
step1: "Primer instal·la una aplicació autenticadora (com {a} o {b}) al teu dispositiu."
|
|
||||||
step2: "Després escaneja el codi QR que es mostra en aquesta pantalla."
|
|
||||||
step2Click: "Fent clic en aquest codi QR et permetrà registrar l'autenticació de doble factor a la teva clau de seguretat o en l'aplicació d'autenticació del teu dispositiu."
|
|
||||||
step2Uri: "Escriu la següent URI si estàs fent servir una aplicació d'escriptori "
|
|
||||||
step3Title: "Escriu un codi d'autenticació"
|
|
||||||
step3: "Escriu el codi d'autenticació (token) que es mostra a la teva aplicació per finalitzar la configuració."
|
|
||||||
setupCompleted: "Configuració terminada"
|
|
||||||
step4: "D'ara endavant quan accedeixis se't demanarà el token que has introduït."
|
|
||||||
securityKeyNotSupported: "El teu navegador no suporta claus de seguretat"
|
|
||||||
removeKeyConfirm: "Esborrar la còpia de seguretat {name}?"
|
|
||||||
renewTOTPCancel: "No, gràcies"
|
renewTOTPCancel: "No, gràcies"
|
||||||
_antennaSources:
|
_antennaSources:
|
||||||
all: "Totes les publicacions"
|
all: "Totes les publicacions"
|
||||||
|
@ -1889,8 +1592,6 @@ _widgets:
|
||||||
chooseList: "Tria una llista"
|
chooseList: "Tria una llista"
|
||||||
_cw:
|
_cw:
|
||||||
show: "Carregar més"
|
show: "Carregar més"
|
||||||
_poll:
|
|
||||||
deadlineTime: "Hor(a)(es)"
|
|
||||||
_visibility:
|
_visibility:
|
||||||
home: "Inici"
|
home: "Inici"
|
||||||
followers: "Seguidors"
|
followers: "Seguidors"
|
||||||
|
|
|
@ -1005,7 +1005,6 @@ resetPasswordConfirm: "Opravdu chcete resetovat heslo?"
|
||||||
sensitiveWords: "Citlivá slova"
|
sensitiveWords: "Citlivá slova"
|
||||||
sensitiveWordsDescription: "Viditelnost všech poznámek obsahujících některé z nakonfigurovaných slov bude automaticky nastavena na \"Domů\". Můžete jich uvést více tak, že je oddělíte pomocí řádků."
|
sensitiveWordsDescription: "Viditelnost všech poznámek obsahujících některé z nakonfigurovaných slov bude automaticky nastavena na \"Domů\". Můžete jich uvést více tak, že je oddělíte pomocí řádků."
|
||||||
sensitiveWordsDescription2: "Použití mezer vytvoří výrazy AND a obklopení klíčových slov lomítky je změní na regulární výraz."
|
sensitiveWordsDescription2: "Použití mezer vytvoří výrazy AND a obklopení klíčových slov lomítky je změní na regulární výraz."
|
||||||
prohibitedWordsDescription2: "Použití mezer vytvoří výrazy AND a obklopení klíčových slov lomítky je změní na regulární výraz."
|
|
||||||
notesSearchNotAvailable: "Vyhledávání poznámek je nedostupné."
|
notesSearchNotAvailable: "Vyhledávání poznámek je nedostupné."
|
||||||
license: "Licence"
|
license: "Licence"
|
||||||
unfavoriteConfirm: "Opravdu chcete odstranit z oblíbených?"
|
unfavoriteConfirm: "Opravdu chcete odstranit z oblíbených?"
|
||||||
|
|
|
@ -1037,7 +1037,6 @@ resetPasswordConfirm: "Wirklich Passwort zurücksetzen?"
|
||||||
sensitiveWords: "Sensible Wörter"
|
sensitiveWords: "Sensible Wörter"
|
||||||
sensitiveWordsDescription: "Die Notizsichtbarkeit aller Notizen, die diese Wörter enthalten, wird automatisch auf \"Startseite\" gesetzt. Durch Zeilenumbrüche können mehrere konfiguriert werden."
|
sensitiveWordsDescription: "Die Notizsichtbarkeit aller Notizen, die diese Wörter enthalten, wird automatisch auf \"Startseite\" gesetzt. Durch Zeilenumbrüche können mehrere konfiguriert werden."
|
||||||
sensitiveWordsDescription2: "Durch die Verwendung von Leerzeichen können AND-Verknüpfungen angegeben werden und durch das Umgeben von Schrägstrichen können reguläre Ausdrücke verwendet werden."
|
sensitiveWordsDescription2: "Durch die Verwendung von Leerzeichen können AND-Verknüpfungen angegeben werden und durch das Umgeben von Schrägstrichen können reguläre Ausdrücke verwendet werden."
|
||||||
prohibitedWordsDescription2: "Durch die Verwendung von Leerzeichen können AND-Verknüpfungen angegeben werden und durch das Umgeben von Schrägstrichen können reguläre Ausdrücke verwendet werden."
|
|
||||||
hiddenTags: "Ausgeblendete Hashtags"
|
hiddenTags: "Ausgeblendete Hashtags"
|
||||||
hiddenTagsDescription: "Die hier eingestellten Tags werden nicht mehr in den Trends angezeigt. Mit der Umschalttaste können mehrere ausgewählt werden."
|
hiddenTagsDescription: "Die hier eingestellten Tags werden nicht mehr in den Trends angezeigt. Mit der Umschalttaste können mehrere ausgewählt werden."
|
||||||
notesSearchNotAvailable: "Die Notizsuche ist nicht verfügbar."
|
notesSearchNotAvailable: "Die Notizsuche ist nicht verfügbar."
|
||||||
|
|
|
@ -1078,7 +1078,6 @@ resetPasswordConfirm: "Really reset your password?"
|
||||||
sensitiveWords: "Sensitive words"
|
sensitiveWords: "Sensitive words"
|
||||||
sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks."
|
sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks."
|
||||||
sensitiveWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
|
sensitiveWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
|
||||||
prohibitedWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
|
|
||||||
hiddenTags: "Hidden hashtags"
|
hiddenTags: "Hidden hashtags"
|
||||||
hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines."
|
hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines."
|
||||||
notesSearchNotAvailable: "Note search is unavailable."
|
notesSearchNotAvailable: "Note search is unavailable."
|
||||||
|
@ -2024,30 +2023,54 @@ _permissions:
|
||||||
"read:flash-likes": "View list of liked Plays"
|
"read:flash-likes": "View list of liked Plays"
|
||||||
"write:flash-likes": "Edit list of liked Plays"
|
"write:flash-likes": "Edit list of liked Plays"
|
||||||
"read:admin:abuse-user-reports": "View user reports"
|
"read:admin:abuse-user-reports": "View user reports"
|
||||||
"write:admin:delete-account": "Delete user account"
|
"write:admin:delete-account": "Delete account"
|
||||||
"write:admin:delete-all-files-of-a-user": "Delete all files of a user"
|
"write:admin:delete-all-files-of-a-user": "Delete all files of a user"
|
||||||
|
"read:admin:index-stats": "View information about database indexes"
|
||||||
|
"read:admin:table-stats": "View information about database tables"
|
||||||
|
"read:admin:user-ips": "View user IP address"
|
||||||
"read:admin:meta": "View instance metadata"
|
"read:admin:meta": "View instance metadata"
|
||||||
"write:admin:reset-password": "Reset user password"
|
"write:admin:reset-password": "Reset user passwords"
|
||||||
"write:admin:send-email": "Send email"
|
"write:admin:resolve-abuse-user-report": "Resolve user reports"
|
||||||
|
"write:admin:send-email": "Send Email"
|
||||||
"read:admin:server-info": "View server info"
|
"read:admin:server-info": "View server info"
|
||||||
"read:admin:show-moderation-log": "View moderation log"
|
"read:admin:show-moderation-log": "View moderation log"
|
||||||
"read:admin:show-user": "View private user info"
|
"read:admin:show-user": "View user information"
|
||||||
"read:admin:show-users": "View private user info"
|
"read:admin:show-users": "View users"
|
||||||
"write:admin:suspend-user": "Suspend user"
|
"write:admin:suspend-user": "Suspend user"
|
||||||
"write:admin:unset-user-avatar": "Remove user avatar"
|
"write:admin:unset-user-avatar": "Remove avatar from user"
|
||||||
"write:admin:unset-user-banner": "Remove user banner"
|
"write:admin:unset-user-banner": "Remove banner from user"
|
||||||
"write:admin:unsuspend-user": "Unsuspend user"
|
"write:admin:unsuspend-user": "Unsuspend user"
|
||||||
"write:admin:meta": "Manage instance metadata"
|
"write:admin:meta": "Edit instance metadata"
|
||||||
"write:admin:user-note": "Manage moderation note"
|
"write:admin:user-note": "Edit user note"
|
||||||
"write:admin:roles": "Manage roles"
|
"write:admin:roles": "Edit roles"
|
||||||
"read:admin:roles": "View roles"
|
"read:admin:roles": "View roles"
|
||||||
"write:admin:relays": "Manage relays"
|
"write:admin:relays": "Edit relays"
|
||||||
"read:admin:relays": "View relays"
|
"read:admin:relays": "View relays"
|
||||||
"write:admin:invite-codes": "Manage invite codes"
|
"write:admin:invite-codes": "Edit invite codes"
|
||||||
"read:admin:invite-codes": "View invite codes"
|
"read:admin:invite-codes": "View invite codes"
|
||||||
"write:admin:announcements": "Manage announcements"
|
"write:admin:announcements": "Edit announcements"
|
||||||
"read:admin:announcements": "View announcements"
|
"read:admin:announcements": "View announcements"
|
||||||
"write:admin:avatar-decorations": "Manage avatar decorations"
|
"write:admin:avatar-decorations": "Edit avatar decorations"
|
||||||
|
"read:admin:avatar-decorations": "View avatar decorations"
|
||||||
|
"write:admin:federation": "Edit remote instance information"
|
||||||
|
"write:admin:account": "Edit users"
|
||||||
|
"read:admin:account": "View information about user"
|
||||||
|
"write:admin:emoji": "Edit emojis"
|
||||||
|
"read:admin:emoji": "View emojis"
|
||||||
|
"write:admin:queue": "Edit queue"
|
||||||
|
"read:admin:queue": "View queue"
|
||||||
|
"write:admin:promo": "Edit promo"
|
||||||
|
"write:admin:drive": "Edit user drive"
|
||||||
|
"read:admin:drive": "View user drive"
|
||||||
|
"read:admin:stream": "Using the Websocket API for Admin"
|
||||||
|
"write:admin:ad": "Edit ads"
|
||||||
|
"read:admin:ad": "View ads"
|
||||||
|
"write:invite-codes": "Create Invitation Code"
|
||||||
|
"read:invite-codes": "View Invitation Code"
|
||||||
|
"write:clip-favorite": "Edit clips and likes"
|
||||||
|
"read:clip-favorite": "View clips and likes"
|
||||||
|
"read:federation": "View information about remote instance"
|
||||||
|
"write:report-abuse": "Report abuse"
|
||||||
_auth:
|
_auth:
|
||||||
shareAccessTitle: "Granting application permissions"
|
shareAccessTitle: "Granting application permissions"
|
||||||
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
||||||
|
|
|
@ -1041,7 +1041,6 @@ resetPasswordConfirm: "¿Realmente quieres cambiar la contraseña?"
|
||||||
sensitiveWords: "Palabras sensibles"
|
sensitiveWords: "Palabras sensibles"
|
||||||
sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de línea"
|
sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de línea"
|
||||||
sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
|
sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
|
||||||
prohibitedWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
|
|
||||||
hiddenTags: "Hashtags ocultos"
|
hiddenTags: "Hashtags ocultos"
|
||||||
hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por línea."
|
hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por línea."
|
||||||
notesSearchNotAvailable: "No se puede buscar una nota"
|
notesSearchNotAvailable: "No se puede buscar una nota"
|
||||||
|
|
|
@ -1038,7 +1038,6 @@ resetPasswordConfirm: "Yakin untuk mereset kata sandimu?"
|
||||||
sensitiveWords: "Kata sensitif"
|
sensitiveWords: "Kata sensitif"
|
||||||
sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru."
|
sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru."
|
||||||
sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
|
sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
|
||||||
prohibitedWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
|
|
||||||
hiddenTags: "Tagar tersembunyi"
|
hiddenTags: "Tagar tersembunyi"
|
||||||
hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris."
|
hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris."
|
||||||
notesSearchNotAvailable: "Pencarian catatan tidak tersedia."
|
notesSearchNotAvailable: "Pencarian catatan tidak tersedia."
|
||||||
|
|
12
locales/index.d.ts
vendored
12
locales/index.d.ts
vendored
|
@ -4329,18 +4329,6 @@ export interface Locale extends ILocale {
|
||||||
* スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。
|
* スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。
|
||||||
*/
|
*/
|
||||||
"sensitiveWordsDescription2": string;
|
"sensitiveWordsDescription2": string;
|
||||||
/**
|
|
||||||
* 禁止ワード
|
|
||||||
*/
|
|
||||||
"prohibitedWords": string;
|
|
||||||
/**
|
|
||||||
* 設定したワードが含まれるノートを投稿しようとした際、エラーとなるようにします。改行で区切って複数設定できます。
|
|
||||||
*/
|
|
||||||
"prohibitedWordsDescription": string;
|
|
||||||
/**
|
|
||||||
* スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。
|
|
||||||
*/
|
|
||||||
"prohibitedWordsDescription2": string;
|
|
||||||
/**
|
/**
|
||||||
* 非表示ハッシュタグ
|
* 非表示ハッシュタグ
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1045,7 +1045,6 @@ resetPasswordConfirm: "Vuoi davvero ripristinare la password?"
|
||||||
sensitiveWords: "Parole esplicite"
|
sensitiveWords: "Parole esplicite"
|
||||||
sensitiveWordsDescription: "Imposta automaticamente \"Home\" alla visibilità delle Note che contengono una qualsiasi parola tra queste configurate. Puoi separarle per riga."
|
sensitiveWordsDescription: "Imposta automaticamente \"Home\" alla visibilità delle Note che contengono una qualsiasi parola tra queste configurate. Puoi separarle per riga."
|
||||||
sensitiveWordsDescription2: "Gli spazi creano la relazione \"E\" tra parole (questo E quello). Racchiudere una parola nelle slash \"/\" la trasforma in Espressione Regolare."
|
sensitiveWordsDescription2: "Gli spazi creano la relazione \"E\" tra parole (questo E quello). Racchiudere una parola nelle slash \"/\" la trasforma in Espressione Regolare."
|
||||||
prohibitedWordsDescription2: "Gli spazi creano la relazione \"E\" tra parole (questo E quello). Racchiudere una parola nelle slash \"/\" la trasforma in Espressione Regolare."
|
|
||||||
hiddenTags: "Hashtag nascosti"
|
hiddenTags: "Hashtag nascosti"
|
||||||
hiddenTagsDescription: "Impedire la visualizzazione del tag impostato nei trend. Puoi impostare più valori, uno per riga."
|
hiddenTagsDescription: "Impedire la visualizzazione del tag impostato nei trend. Puoi impostare più valori, uno per riga."
|
||||||
notesSearchNotAvailable: "Non è possibile cercare tra le Note."
|
notesSearchNotAvailable: "Non è possibile cercare tra le Note."
|
||||||
|
|
|
@ -1078,9 +1078,6 @@ resetPasswordConfirm: "パスワードリセットしますか?"
|
||||||
sensitiveWords: "センシティブワード"
|
sensitiveWords: "センシティブワード"
|
||||||
sensitiveWordsDescription: "設定したワードが含まれるノートの公開範囲をホームにします。改行で区切って複数設定できます。"
|
sensitiveWordsDescription: "設定したワードが含まれるノートの公開範囲をホームにします。改行で区切って複数設定できます。"
|
||||||
sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。"
|
sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。"
|
||||||
prohibitedWords: "禁止ワード"
|
|
||||||
prohibitedWordsDescription: "設定したワードが含まれるノートを投稿しようとした際、エラーとなるようにします。改行で区切って複数設定できます。"
|
|
||||||
prohibitedWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。"
|
|
||||||
hiddenTags: "非表示ハッシュタグ"
|
hiddenTags: "非表示ハッシュタグ"
|
||||||
hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。"
|
hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。"
|
||||||
notesSearchNotAvailable: "ノート検索は利用できません。"
|
notesSearchNotAvailable: "ノート検索は利用できません。"
|
||||||
|
|
|
@ -1043,7 +1043,6 @@ resetPasswordConfirm: "パスワード作り直すんでええな?"
|
||||||
sensitiveWords: "けったいな単語"
|
sensitiveWords: "けったいな単語"
|
||||||
sensitiveWordsDescription: "設定した単語が入っとるノートの公開範囲をホームにしたるわ。改行で区切ったら複数設定できるで。"
|
sensitiveWordsDescription: "設定した単語が入っとるノートの公開範囲をホームにしたるわ。改行で区切ったら複数設定できるで。"
|
||||||
sensitiveWordsDescription2: "スペースで区切るとAND指定、キーワードをスラッシュで囲んだら正規表現や。"
|
sensitiveWordsDescription2: "スペースで区切るとAND指定、キーワードをスラッシュで囲んだら正規表現や。"
|
||||||
prohibitedWordsDescription2: "スペースで区切るとAND指定、キーワードをスラッシュで囲んだら正規表現や。"
|
|
||||||
hiddenTags: "見えてへんハッシュタグ"
|
hiddenTags: "見えてへんハッシュタグ"
|
||||||
hiddenTagsDescription: "設定したタグを最近流行りのとこに見えんようにすんで。複数設定するときは改行で区切ってな。"
|
hiddenTagsDescription: "設定したタグを最近流行りのとこに見えんようにすんで。複数設定するときは改行で区切ってな。"
|
||||||
notesSearchNotAvailable: "なんかノート探せへん。"
|
notesSearchNotAvailable: "なんかノート探せへん。"
|
||||||
|
|
|
@ -1041,7 +1041,6 @@ resetPasswordConfirm: "비밀번호를 재설정하시겠습니까?"
|
||||||
sensitiveWords: "민감한 단어"
|
sensitiveWords: "민감한 단어"
|
||||||
sensitiveWordsDescription: "설정한 단어가 포함된 노트의 공개 범위를 '홈'으로 강제합니다. 개행으로 구분하여 여러 개를 지정할 수 있습니다."
|
sensitiveWordsDescription: "설정한 단어가 포함된 노트의 공개 범위를 '홈'으로 강제합니다. 개행으로 구분하여 여러 개를 지정할 수 있습니다."
|
||||||
sensitiveWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다."
|
sensitiveWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다."
|
||||||
prohibitedWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다."
|
|
||||||
hiddenTags: "숨긴 해시태그"
|
hiddenTags: "숨긴 해시태그"
|
||||||
hiddenTagsDescription: "설정한 태그를 트렌드에 표시하지 않도록 합니다. 줄 바꿈으로 하나씩 나눠서 설정할 수 있습니다."
|
hiddenTagsDescription: "설정한 태그를 트렌드에 표시하지 않도록 합니다. 줄 바꿈으로 하나씩 나눠서 설정할 수 있습니다."
|
||||||
notesSearchNotAvailable: "노트 검색을 이용하실 수 없습니다."
|
notesSearchNotAvailable: "노트 검색을 이용하실 수 없습니다."
|
||||||
|
|
|
@ -1015,7 +1015,6 @@ resetPasswordConfirm: "Сбросить пароль?"
|
||||||
sensitiveWords: "Чувствительные слова"
|
sensitiveWords: "Чувствительные слова"
|
||||||
sensitiveWordsDescription: "Установите общедоступный диапазон заметки, содержащей заданное слово, на домашний. Можно сделать несколько настроек, разделив их переносами строк."
|
sensitiveWordsDescription: "Установите общедоступный диапазон заметки, содержащей заданное слово, на домашний. Можно сделать несколько настроек, разделив их переносами строк."
|
||||||
sensitiveWordsDescription2: "Разделение пробелом создаёт спецификацию AND, а разделение косой чертой создаёт регулярное выражение."
|
sensitiveWordsDescription2: "Разделение пробелом создаёт спецификацию AND, а разделение косой чертой создаёт регулярное выражение."
|
||||||
prohibitedWordsDescription2: "Разделение пробелом создаёт спецификацию AND, а разделение косой чертой создаёт регулярное выражение."
|
|
||||||
notesSearchNotAvailable: "Поиск заметок недоступен"
|
notesSearchNotAvailable: "Поиск заметок недоступен"
|
||||||
license: "Лицензия"
|
license: "Лицензия"
|
||||||
unfavoriteConfirm: "Удалить избранное?"
|
unfavoriteConfirm: "Удалить избранное?"
|
||||||
|
|
|
@ -1041,7 +1041,6 @@ resetPasswordConfirm: "รีเซ็ตรหัสผ่านของคุ
|
||||||
sensitiveWords: "คำที่มีเนื้อหาละเอียดอ่อน"
|
sensitiveWords: "คำที่มีเนื้อหาละเอียดอ่อน"
|
||||||
sensitiveWordsDescription: "การเปิดเผยโน้ตทั้งหมดที่มีคำที่กำหนดค่าไว้จะถูกตั้งค่าเป็น \"หน้าแรก\" โดยอัตโนมัติ คุณยังสามารถแสดงหลายรายการได้โดยแยกรายการโดยใช้ตัวแบ่งบรรทัดได้นะ"
|
sensitiveWordsDescription: "การเปิดเผยโน้ตทั้งหมดที่มีคำที่กำหนดค่าไว้จะถูกตั้งค่าเป็น \"หน้าแรก\" โดยอัตโนมัติ คุณยังสามารถแสดงหลายรายการได้โดยแยกรายการโดยใช้ตัวแบ่งบรรทัดได้นะ"
|
||||||
sensitiveWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
|
sensitiveWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
|
||||||
prohibitedWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
|
|
||||||
hiddenTags: "แฮชแท็กที่ซ่อนอยู่"
|
hiddenTags: "แฮชแท็กที่ซ่อนอยู่"
|
||||||
hiddenTagsDescription: "เลือกแท็กที่จะไม่แสดงในรายการเทรนด์ สามารถลงทะเบียนหลายแท็กได้โดยขึ้นบรรทัดใหม่"
|
hiddenTagsDescription: "เลือกแท็กที่จะไม่แสดงในรายการเทรนด์ สามารถลงทะเบียนหลายแท็กได้โดยขึ้นบรรทัดใหม่"
|
||||||
notesSearchNotAvailable: "การค้นหาโน้ตไม่พร้อมใช้งาน"
|
notesSearchNotAvailable: "การค้นหาโน้ตไม่พร้อมใช้งาน"
|
||||||
|
|
|
@ -1041,7 +1041,6 @@ resetPasswordConfirm: "确定重置密码?"
|
||||||
sensitiveWords: "敏感词"
|
sensitiveWords: "敏感词"
|
||||||
sensitiveWordsDescription: "将包含设置词的帖子的可见范围设置为首页。可以通过用换行符分隔来设置多个。"
|
sensitiveWordsDescription: "将包含设置词的帖子的可见范围设置为首页。可以通过用换行符分隔来设置多个。"
|
||||||
sensitiveWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
|
sensitiveWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
|
||||||
prohibitedWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
|
|
||||||
hiddenTags: "隐藏标签"
|
hiddenTags: "隐藏标签"
|
||||||
hiddenTagsDescription: "设定的标签将不会在时间线上显示。可使用换行来设置多个标签。"
|
hiddenTagsDescription: "设定的标签将不会在时间线上显示。可使用换行来设置多个标签。"
|
||||||
notesSearchNotAvailable: "帖子检索不可用"
|
notesSearchNotAvailable: "帖子检索不可用"
|
||||||
|
|
|
@ -1041,7 +1041,6 @@ resetPasswordConfirm: "重設密碼?"
|
||||||
sensitiveWords: "敏感詞"
|
sensitiveWords: "敏感詞"
|
||||||
sensitiveWordsDescription: "將含有設定詞彙的貼文可見性設為發送至首頁。可以用換行來進行複數的設定。"
|
sensitiveWordsDescription: "將含有設定詞彙的貼文可見性設為發送至首頁。可以用換行來進行複數的設定。"
|
||||||
sensitiveWordsDescription2: "空格代表「以及」(AND),斜線包圍關鍵字代表使用正規表達式。"
|
sensitiveWordsDescription2: "空格代表「以及」(AND),斜線包圍關鍵字代表使用正規表達式。"
|
||||||
prohibitedWordsDescription2: "空格代表「以及」(AND),斜線包圍關鍵字代表使用正規表達式。"
|
|
||||||
hiddenTags: "隱藏標籤"
|
hiddenTags: "隱藏標籤"
|
||||||
hiddenTagsDescription: "設定的標籤不會在趨勢中顯示,換行可以設定多個標籤。"
|
hiddenTagsDescription: "設定的標籤不會在趨勢中顯示,換行可以設定多個標籤。"
|
||||||
notesSearchNotAvailable: "無法使用搜尋貼文功能。"
|
notesSearchNotAvailable: "無法使用搜尋貼文功能。"
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "sharkey",
|
"name": "sharkey",
|
||||||
"version": "2024.2.0-beta.11",
|
"version": "2024.2.0-beta.10",
|
||||||
"codename": "shonk",
|
"codename": "shonk",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.joinsharkey.org/Sharkey/Sharkey.git"
|
"url": "https://git.joinsharkey.org/Sharkey/Sharkey.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.15.1",
|
"packageManager": "pnpm@8.12.1",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/frontend",
|
"packages/frontend",
|
||||||
"packages/backend",
|
"packages/backend",
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class prohibitedWords1707429690000 {
|
|
||||||
name = 'prohibitedWords1707429690000'
|
|
||||||
|
|
||||||
async up(queryRunner) {
|
|
||||||
await queryRunner.query(`ALTER TABLE "meta" ADD "prohibitedWords" character varying(1024) array NOT NULL DEFAULT '{}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async down(queryRunner) {
|
|
||||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "prohibitedWords"`);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -84,7 +84,7 @@
|
||||||
"@nestjs/testing": "10.2.10",
|
"@nestjs/testing": "10.2.10",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@transfem-org/sfm-js": "0.24.4",
|
"@transfem-org/sfm-js": "0.24.4",
|
||||||
"@simplewebauthn/server": "9.0.2",
|
"@simplewebauthn/server": "9.0.1",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
"@smithy/node-http-handler": "2.1.10",
|
"@smithy/node-http-handler": "2.1.10",
|
||||||
"@swc/cli": "0.1.63",
|
"@swc/cli": "0.1.63",
|
||||||
|
@ -98,12 +98,12 @@
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "5.1.9",
|
"bullmq": "5.1.5",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.2",
|
"cbor": "9.0.1",
|
||||||
"chalk": "5.3.0",
|
"chalk": "5.3.0",
|
||||||
"chalk-template": "1.1.0",
|
"chalk-template": "1.1.0",
|
||||||
"chokidar": "3.6.0",
|
"chokidar": "3.5.3",
|
||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"color-convert": "2.0.1",
|
"color-convert": "2.0.1",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
|
@ -205,7 +205,7 @@
|
||||||
"@types/jsrsasign": "10.5.12",
|
"@types/jsrsasign": "10.5.12",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "20.11.17",
|
"@types/node": "20.11.10",
|
||||||
"@types/node-fetch": "3.0.3",
|
"@types/node-fetch": "3.0.3",
|
||||||
"@types/nodemailer": "6.4.14",
|
"@types/nodemailer": "6.4.14",
|
||||||
"@types/oauth": "0.9.4",
|
"@types/oauth": "0.9.4",
|
||||||
|
|
|
@ -408,7 +408,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public checkDuplicate(name: string): Promise<boolean> {
|
public checkDuplicate(name: string): Promise<boolean> {
|
||||||
return this.emojisRepository.exists({ where: { name, host: IsNull() } });
|
return this.emojisRepository.exist({ where: { name, host: IsNull() } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
|
@ -163,7 +163,7 @@ export class HashtagService {
|
||||||
const instance = await this.metaService.fetch();
|
const instance = await this.metaService.fetch();
|
||||||
const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t));
|
const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t));
|
||||||
if (hiddenTags.includes(hashtag)) return;
|
if (hiddenTags.includes(hashtag)) return;
|
||||||
if (this.utilityService.isKeyWordIncluded(hashtag, instance.sensitiveWords)) return;
|
if (this.utilityService.isSensitiveWordIncluded(hashtag, instance.sensitiveWords)) return;
|
||||||
|
|
||||||
// YYYYMMDDHHmm (10分間隔)
|
// YYYYMMDDHHmm (10分間隔)
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
|
@ -153,8 +153,6 @@ type Option = {
|
||||||
export class NoteCreateService implements OnApplicationShutdown {
|
export class NoteCreateService implements OnApplicationShutdown {
|
||||||
#shutdownController = new AbortController();
|
#shutdownController = new AbortController();
|
||||||
|
|
||||||
public static ContainsProhibitedWordsError = class extends Error {};
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
private config: Config,
|
private config: Config,
|
||||||
|
@ -259,7 +257,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
|
|
||||||
if (data.visibility === 'public' && data.channel == null) {
|
if (data.visibility === 'public' && data.channel == null) {
|
||||||
const sensitiveWords = meta.sensitiveWords;
|
const sensitiveWords = meta.sensitiveWords;
|
||||||
if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
|
@ -431,19 +429,13 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
|
|
||||||
if (data.visibility === 'public' && data.channel == null) {
|
if (data.visibility === 'public' && data.channel == null) {
|
||||||
const sensitiveWords = meta.sensitiveWords;
|
const sensitiveWords = meta.sensitiveWords;
|
||||||
if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.host) {
|
|
||||||
if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
|
|
||||||
throw new NoteCreateService.ContainsProhibitedWordsError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
|
const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
|
||||||
|
|
||||||
if (data.visibility === 'public' && inSilencedInstance && user.host !== null) {
|
if (data.visibility === 'public' && inSilencedInstance && user.host !== null) {
|
||||||
|
@ -803,7 +795,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
});
|
});
|
||||||
// 通知
|
// 通知
|
||||||
if (data.reply.userHost === null) {
|
if (data.reply.userHost === null) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: data.reply.userId,
|
userId: data.reply.userId,
|
||||||
threadId: data.reply.threadId ?? data.reply.id,
|
threadId: data.reply.threadId ?? data.reply.id,
|
||||||
|
@ -838,7 +830,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
|
|
||||||
// Notify
|
// Notify
|
||||||
if (data.renote.userHost === null) {
|
if (data.renote.userHost === null) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: data.renote.userId,
|
userId: data.renote.userId,
|
||||||
threadId: data.renote.threadId ?? data.renote.id,
|
threadId: data.renote.threadId ?? data.renote.id,
|
||||||
|
@ -1065,7 +1057,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
@bindThis
|
@bindThis
|
||||||
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
|
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
|
||||||
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: u.id,
|
userId: u.id,
|
||||||
threadId: note.threadId ?? note.id,
|
threadId: note.threadId ?? note.id,
|
||||||
|
|
|
@ -145,8 +145,6 @@ type Option = {
|
||||||
export class NoteEditService implements OnApplicationShutdown {
|
export class NoteEditService implements OnApplicationShutdown {
|
||||||
#shutdownController = new AbortController();
|
#shutdownController = new AbortController();
|
||||||
|
|
||||||
public static ContainsProhibitedWordsError = class extends Error {};
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
private config: Config,
|
private config: Config,
|
||||||
|
@ -268,7 +266,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
||||||
|
|
||||||
if (data.visibility === 'public' && data.channel == null) {
|
if (data.visibility === 'public' && data.channel == null) {
|
||||||
const sensitiveWords = meta.sensitiveWords;
|
const sensitiveWords = meta.sensitiveWords;
|
||||||
if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
|
@ -614,7 +612,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
||||||
if (data.reply) {
|
if (data.reply) {
|
||||||
// 通知
|
// 通知
|
||||||
if (data.reply.userHost === null) {
|
if (data.reply.userHost === null) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: data.reply.userId,
|
userId: data.reply.userId,
|
||||||
threadId: data.reply.threadId ?? data.reply.id,
|
threadId: data.reply.threadId ?? data.reply.id,
|
||||||
|
@ -649,7 +647,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
||||||
|
|
||||||
// Notify
|
// Notify
|
||||||
if (data.renote.userHost === null) {
|
if (data.renote.userHost === null) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: data.renote.userId,
|
userId: data.renote.userId,
|
||||||
threadId: data.renote.threadId ?? data.renote.id,
|
threadId: data.renote.threadId ?? data.renote.id,
|
||||||
|
@ -753,7 +751,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
||||||
@bindThis
|
@bindThis
|
||||||
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
|
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
|
||||||
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: u.id,
|
userId: u.id,
|
||||||
threadId: note.threadId ?? note.id,
|
threadId: note.threadId ?? note.id,
|
||||||
|
|
|
@ -49,7 +49,7 @@ export class NoteReadService implements OnApplicationShutdown {
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// スレッドミュート
|
// スレッドミュート
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
threadId: note.threadId ?? note.id,
|
threadId: note.threadId ?? note.id,
|
||||||
|
@ -70,7 +70,7 @@ export class NoteReadService implements OnApplicationShutdown {
|
||||||
|
|
||||||
// 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する
|
// 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する
|
||||||
setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => {
|
setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => {
|
||||||
const exist = await this.noteUnreadsRepository.exists({ where: { id: unread.id } });
|
const exist = await this.noteUnreadsRepository.exist({ where: { id: unread.id } });
|
||||||
|
|
||||||
if (!exist) return;
|
if (!exist) return;
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ export class ReactionService {
|
||||||
|
|
||||||
// リアクションされたユーザーがローカルユーザーなら通知を作成
|
// リアクションされたユーザーがローカルユーザーなら通知を作成
|
||||||
if (note.userHost === null) {
|
if (note.userHost === null) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: note.userId,
|
userId: note.userId,
|
||||||
threadId: note.threadId ?? note.id,
|
threadId: note.threadId ?? note.id,
|
||||||
|
|
|
@ -77,12 +77,12 @@ export class SignupService {
|
||||||
const secret = generateUserToken();
|
const secret = generateUserToken();
|
||||||
|
|
||||||
// Check username duplication
|
// Check username duplication
|
||||||
if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
||||||
throw new Error('DUPLICATED_USERNAME');
|
throw new Error('DUPLICATED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check deleted username duplication
|
// Check deleted username duplication
|
||||||
if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
|
if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) {
|
||||||
throw new Error('USED_USERNAME');
|
throw new Error('USED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
let autoAccept = false;
|
let autoAccept = false;
|
||||||
|
|
||||||
// 鍵アカウントであっても、既にフォローされていた場合はスルー
|
// 鍵アカウントであっても、既にフォローされていた場合はスルー
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
|
@ -156,7 +156,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
|
|
||||||
// フォローしているユーザーは自動承認オプション
|
// フォローしているユーザーは自動承認オプション
|
||||||
if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) {
|
if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) {
|
||||||
const isFollowed = await this.followingsRepository.exists({
|
const isFollowed = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: followee.id,
|
followerId: followee.id,
|
||||||
followeeId: follower.id,
|
followeeId: follower.id,
|
||||||
|
@ -170,7 +170,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
if (followee.isLocked && !autoAccept) {
|
if (followee.isLocked && !autoAccept) {
|
||||||
autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs(
|
autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs(
|
||||||
follower,
|
follower,
|
||||||
(oldSrc, newSrc) => this.followingsRepository.exists({
|
(oldSrc, newSrc) => this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
followerId: newSrc.id,
|
followerId: newSrc.id,
|
||||||
|
@ -233,7 +233,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
|
|
||||||
this.cacheService.userFollowingsCache.refresh(follower.id);
|
this.cacheService.userFollowingsCache.refresh(follower.id);
|
||||||
|
|
||||||
const requestExist = await this.followRequestsRepository.exists({
|
const requestExist = await this.followRequestsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
|
@ -531,7 +531,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestExist = await this.followRequestsRepository.exists({
|
const requestExist = await this.followRequestsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
|
|
|
@ -43,13 +43,13 @@ export class UtilityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public isKeyWordIncluded(text: string, keyWords: string[]): boolean {
|
public isSensitiveWordIncluded(text: string, sensitiveWords: string[]): boolean {
|
||||||
if (keyWords.length === 0) return false;
|
if (sensitiveWords.length === 0) return false;
|
||||||
if (text === '') return false;
|
if (text === '') return false;
|
||||||
|
|
||||||
const regexpregexp = /^\/(.+)\/(.*)$/;
|
const regexpregexp = /^\/(.+)\/(.*)$/;
|
||||||
|
|
||||||
const matched = keyWords.some(filter => {
|
const matched = sensitiveWords.some(filter => {
|
||||||
// represents RegExp
|
// represents RegExp
|
||||||
const regexp = filter.match(regexpregexp);
|
const regexp = filter.match(regexpregexp);
|
||||||
// This should never happen due to input sanitisation.
|
// This should never happen due to input sanitisation.
|
||||||
|
|
|
@ -629,7 +629,7 @@ export class ApInboxService {
|
||||||
return 'skip: follower not found';
|
return 'skip: follower not found';
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: actor.id,
|
followeeId: actor.id,
|
||||||
|
@ -686,14 +686,14 @@ export class ApInboxService {
|
||||||
return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません';
|
return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません';
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestExist = await this.followRequestsRepository.exists({
|
const requestExist = await this.followRequestsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: actor.id,
|
followerId: actor.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: actor.id,
|
followerId: actor.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
|
|
|
@ -345,7 +345,7 @@ export class ApRendererService {
|
||||||
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
||||||
|
|
||||||
if (inReplyToNote != null) {
|
if (inReplyToNote != null) {
|
||||||
const inReplyToUserExist = await this.usersRepository.exists({ where: { id: inReplyToNote.userId } });
|
const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } });
|
||||||
|
|
||||||
if (inReplyToUserExist) {
|
if (inReplyToUserExist) {
|
||||||
if (inReplyToNote.uri) {
|
if (inReplyToNote.uri) {
|
||||||
|
@ -636,7 +636,7 @@ export class ApRendererService {
|
||||||
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
||||||
|
|
||||||
if (inReplyToNote != null) {
|
if (inReplyToNote != null) {
|
||||||
const inReplyToUserExist = await this.usersRepository.exists({ where: { id: inReplyToNote.userId } });
|
const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } });
|
||||||
|
|
||||||
if (inReplyToUserExist) {
|
if (inReplyToUserExist) {
|
||||||
if (inReplyToNote.uri) {
|
if (inReplyToNote.uri) {
|
||||||
|
|
|
@ -51,14 +51,14 @@ export class ChannelEntityService {
|
||||||
|
|
||||||
const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null;
|
const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null;
|
||||||
|
|
||||||
const isFollowing = meId ? await this.channelFollowingsRepository.exists({
|
const isFollowing = meId ? await this.channelFollowingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
followeeId: channel.id,
|
followeeId: channel.id,
|
||||||
},
|
},
|
||||||
}) : false;
|
}) : false;
|
||||||
|
|
||||||
const isFavorited = meId ? await this.channelFavoritesRepository.exists({
|
const isFavorited = meId ? await this.channelFavoritesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: meId,
|
userId: meId,
|
||||||
channelId: channel.id,
|
channelId: channel.id,
|
||||||
|
|
|
@ -46,7 +46,7 @@ export class ClipEntityService {
|
||||||
description: clip.description,
|
description: clip.description,
|
||||||
isPublic: clip.isPublic,
|
isPublic: clip.isPublic,
|
||||||
favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }),
|
favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }),
|
||||||
isFavorited: meId ? await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: meId } }) : undefined,
|
isFavorited: meId ? await this.clipFavoritesRepository.exist({ where: { clipId: clip.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ export class FlashEntityService {
|
||||||
summary: flash.summary,
|
summary: flash.summary,
|
||||||
script: flash.script,
|
script: flash.script,
|
||||||
likedCount: flash.likedCount,
|
likedCount: flash.likedCount,
|
||||||
isLiked: meId ? await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } }) : undefined,
|
isLiked: meId ? await this.flashLikesRepository.exist({ where: { flashId: flash.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ export class GalleryPostEntityService {
|
||||||
tags: post.tags.length > 0 ? post.tags : undefined,
|
tags: post.tags.length > 0 ? post.tags : undefined,
|
||||||
isSensitive: post.isSensitive,
|
isSensitive: post.isSensitive,
|
||||||
likedCount: post.likedCount,
|
likedCount: post.likedCount,
|
||||||
isLiked: meId ? await this.galleryLikesRepository.exists({ where: { postId: post.id, userId: meId } }) : undefined,
|
isLiked: meId ? await this.galleryLikesRepository.exist({ where: { postId: post.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
hide = false;
|
hide = false;
|
||||||
} else {
|
} else {
|
||||||
if (packedNote.renote) {
|
if (packedNote.renote) {
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followeeId: packedNote.renote.userId,
|
followeeId: packedNote.renote.userId,
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
|
@ -124,7 +124,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
hide = !isFollowing;
|
hide = !isFollowing;
|
||||||
} else {
|
} else {
|
||||||
// フォロワーかどうか
|
// フォロワーかどうか
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followeeId: packedNote.userId,
|
followeeId: packedNote.userId,
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
|
|
|
@ -104,7 +104,7 @@ export class PageEntityService {
|
||||||
eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null,
|
eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null,
|
||||||
attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null)),
|
attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null)),
|
||||||
likedCount: page.likedCount,
|
likedCount: page.likedCount,
|
||||||
isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined,
|
isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,43 +153,43 @@ export class UserEntityService implements OnModuleInit {
|
||||||
followerId: me,
|
followerId: me,
|
||||||
followeeId: target,
|
followeeId: target,
|
||||||
}),
|
}),
|
||||||
this.followingsRepository.exists({
|
this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: target,
|
followerId: target,
|
||||||
followeeId: me,
|
followeeId: me,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.followRequestsRepository.exists({
|
this.followRequestsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: me,
|
followerId: me,
|
||||||
followeeId: target,
|
followeeId: target,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.followRequestsRepository.exists({
|
this.followRequestsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: target,
|
followerId: target,
|
||||||
followeeId: me,
|
followeeId: me,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.blockingsRepository.exists({
|
this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: me,
|
blockerId: me,
|
||||||
blockeeId: target,
|
blockeeId: target,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.blockingsRepository.exists({
|
this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: target,
|
blockerId: target,
|
||||||
blockeeId: me,
|
blockeeId: me,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.mutingsRepository.exists({
|
this.mutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
muterId: me,
|
muterId: me,
|
||||||
muteeId: target,
|
muteeId: target,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.renoteMutingsRepository.exists({
|
this.renoteMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
muterId: me,
|
muterId: me,
|
||||||
muteeId: target,
|
muteeId: target,
|
||||||
|
@ -216,7 +216,7 @@ export class UserEntityService implements OnModuleInit {
|
||||||
/*
|
/*
|
||||||
const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId);
|
const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId);
|
||||||
|
|
||||||
const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exists({
|
const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
antennaId: In(myAntennas.map(x => x.id)),
|
antennaId: In(myAntennas.map(x => x.id)),
|
||||||
read: false,
|
read: false,
|
||||||
|
|
|
@ -76,11 +76,6 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public sensitiveWords: string[];
|
public sensitiveWords: string[];
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 1024, array: true, default: '{}',
|
|
||||||
})
|
|
||||||
public prohibitedWords: string[];
|
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024, array: true, default: '{}',
|
length: 1024, array: true, default: '{}',
|
||||||
})
|
})
|
||||||
|
|
|
@ -176,12 +176,12 @@ export class SignupApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance.emailRequiredForSignup) {
|
if (instance.emailRequiredForSignup) {
|
||||||
if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
||||||
throw new FastifyReplyError(400, 'DUPLICATED_USERNAME');
|
throw new FastifyReplyError(400, 'DUPLICATED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check deleted username duplication
|
// Check deleted username duplication
|
||||||
if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
|
if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) {
|
||||||
throw new FastifyReplyError(400, 'USED_USERNAME');
|
throw new FastifyReplyError(400, 'USED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,13 +160,6 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
prohibitedWords: {
|
|
||||||
type: 'array',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
items: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
bannedEmailDomains: {
|
bannedEmailDomains: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: true, nullable: false,
|
optional: true, nullable: false,
|
||||||
|
@ -556,7 +549,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
blockedHosts: instance.blockedHosts,
|
blockedHosts: instance.blockedHosts,
|
||||||
silencedHosts: instance.silencedHosts,
|
silencedHosts: instance.silencedHosts,
|
||||||
sensitiveWords: instance.sensitiveWords,
|
sensitiveWords: instance.sensitiveWords,
|
||||||
prohibitedWords: instance.prohibitedWords,
|
|
||||||
preservedUsernames: instance.preservedUsernames,
|
preservedUsernames: instance.preservedUsernames,
|
||||||
bubbleInstances: instance.bubbleInstances,
|
bubbleInstances: instance.bubbleInstances,
|
||||||
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
||||||
|
|
|
@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
|
|
||||||
const exist = await this.promoNotesRepository.exists({ where: { noteId: note.id } });
|
const exist = await this.promoNotesRepository.exist({ where: { noteId: note.id } });
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
throw new ApiError(meta.errors.alreadyPromoted);
|
throw new ApiError(meta.errors.alreadyPromoted);
|
||||||
|
|
|
@ -41,11 +41,6 @@ export const paramDef = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
prohibitedWords: {
|
|
||||||
type: 'array', nullable: true, items: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
|
themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
|
||||||
mascotImageUrl: { type: 'string', nullable: true },
|
mascotImageUrl: { type: 'string', nullable: true },
|
||||||
bannerUrl: { type: 'string', nullable: true },
|
bannerUrl: { type: 'string', nullable: true },
|
||||||
|
@ -190,9 +185,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (Array.isArray(ps.sensitiveWords)) {
|
if (Array.isArray(ps.sensitiveWords)) {
|
||||||
set.sensitiveWords = ps.sensitiveWords.filter(Boolean);
|
set.sensitiveWords = ps.sensitiveWords.filter(Boolean);
|
||||||
}
|
}
|
||||||
if (Array.isArray(ps.prohibitedWords)) {
|
|
||||||
set.prohibitedWords = ps.prohibitedWords.filter(Boolean);
|
|
||||||
}
|
|
||||||
if (Array.isArray(ps.silencedHosts)) {
|
if (Array.isArray(ps.silencedHosts)) {
|
||||||
let lastValue = '';
|
let lastValue = '';
|
||||||
set.silencedHosts = ps.silencedHosts.sort().filter((h) => {
|
set.silencedHosts = ps.silencedHosts.sort().filter((h) => {
|
||||||
|
|
|
@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
const accessToken = secureRndstr(32);
|
const accessToken = secureRndstr(32);
|
||||||
|
|
||||||
// Fetch exist access token
|
// Fetch exist access token
|
||||||
const exist = await this.accessTokensRepository.exists({
|
const exist = await this.accessTokensRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
appId: session.appId,
|
appId: session.appId,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -88,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if already blocking
|
// Check if already blocking
|
||||||
const exist = await this.blockingsRepository.exists({
|
const exist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: blocker.id,
|
blockerId: blocker.id,
|
||||||
blockeeId: blockee.id,
|
blockeeId: blockee.id,
|
||||||
|
|
|
@ -88,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check not blocking
|
// Check not blocking
|
||||||
const exist = await this.blockingsRepository.exists({
|
const exist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: blocker.id,
|
blockerId: blocker.id,
|
||||||
blockeeId: blockee.id,
|
blockeeId: blockee.id,
|
||||||
|
|
|
@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw new ApiError(meta.errors.noSuchClip);
|
throw new ApiError(meta.errors.noSuchClip);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.clipFavoritesRepository.exists({
|
const exist = await this.clipFavoritesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
clipId: clip.id,
|
clipId: clip.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -38,7 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private driveFilesRepository: DriveFilesRepository,
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const exist = await this.driveFilesRepository.exists({
|
const exist = await this.driveFilesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
md5: ps.md5,
|
md5: ps.md5,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
// if already liked
|
// if already liked
|
||||||
const exist = await this.flashLikesRepository.exists({
|
const exist = await this.flashLikesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
flashId: flash.id,
|
flashId: flash.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
const exist = await this.followingsRepository.exists({
|
const exist = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
|
|
|
@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check not following
|
// Check not following
|
||||||
const exist = await this.followingsRepository.exists({
|
const exist = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
|
|
|
@ -72,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
// if already liked
|
// if already liked
|
||||||
const exist = await this.galleryLikesRepository.exists({
|
const exist = await this.galleryLikesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
postId: post.id,
|
postId: post.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
private downloadService: DownloadService,
|
private downloadService: DownloadService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const userExist = await this.usersRepository.exists({ where: { id: me.id } });
|
const userExist = await this.usersRepository.exist({ where: { id: me.id } });
|
||||||
if (!userExist) throw new ApiError(meta.errors.noSuchUser);
|
if (!userExist) throw new ApiError(meta.errors.noSuchUser);
|
||||||
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
||||||
if (file === null) throw new ApiError(meta.errors.noSuchFile);
|
if (file === null) throw new ApiError(meta.errors.noSuchFile);
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
if (ps.tokenId) {
|
if (ps.tokenId) {
|
||||||
const tokenExist = await this.accessTokensRepository.exists({ where: { id: ps.tokenId } });
|
const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } });
|
||||||
|
|
||||||
if (tokenExist) {
|
if (tokenExist) {
|
||||||
await this.accessTokensRepository.delete({
|
await this.accessTokensRepository.delete({
|
||||||
|
@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (ps.token) {
|
} else if (ps.token) {
|
||||||
const tokenExist = await this.accessTokensRepository.exists({ where: { token: ps.token } });
|
const tokenExist = await this.accessTokensRepository.exist({ where: { token: ps.token } });
|
||||||
|
|
||||||
if (tokenExist) {
|
if (tokenExist) {
|
||||||
await this.accessTokensRepository.delete({
|
await this.accessTokensRepository.delete({
|
||||||
|
|
|
@ -83,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if already muting
|
// Check if already muting
|
||||||
const exist = await this.mutingsRepository.exists({
|
const exist = await this.mutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
muterId: muter.id,
|
muterId: muter.id,
|
||||||
muteeId: mutee.id,
|
muteeId: mutee.id,
|
||||||
|
|
|
@ -17,8 +17,6 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { NoteCreateService } from '@/core/NoteCreateService.js';
|
import { NoteCreateService } from '@/core/NoteCreateService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { isPureRenote } from '@/misc/is-pure-renote.js';
|
import { isPureRenote } from '@/misc/is-pure-renote.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -119,12 +117,6 @@ export const meta = {
|
||||||
code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL',
|
code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL',
|
||||||
id: '33510210-8452-094c-6227-4a6c05d99f00',
|
id: '33510210-8452-094c-6227-4a6c05d99f00',
|
||||||
},
|
},
|
||||||
|
|
||||||
containsProhibitedWords: {
|
|
||||||
message: 'Cannot post because it contains prohibited words.',
|
|
||||||
code: 'CONTAINS_PROHIBITED_WORDS',
|
|
||||||
id: 'aa6e01d3-a85c-669d-758a-76aab43af334',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@ -279,7 +271,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (renote.userId !== me.id) {
|
if (renote.userId !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exists({
|
const blockExist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: renote.userId,
|
blockerId: renote.userId,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
|
@ -327,7 +319,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (reply.userId !== me.id) {
|
if (reply.userId !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exists({
|
const blockExist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: reply.userId,
|
blockerId: reply.userId,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
|
@ -359,7 +351,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
// 投稿を作成
|
// 投稿を作成
|
||||||
try {
|
|
||||||
const note = await this.noteCreateService.create(me, {
|
const note = await this.noteCreateService.create(me, {
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
files: files,
|
files: files,
|
||||||
|
@ -385,14 +376,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
return {
|
return {
|
||||||
createdNote: await this.noteEntityService.pack(note, me),
|
createdNote: await this.noteEntityService.pack(note, me),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
|
||||||
// TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい
|
|
||||||
if (e instanceof NoteCreateService.ContainsProhibitedWordsError) {
|
|
||||||
throw new ApiError(meta.errors.containsProhibitedWords);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,7 +311,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (renote.userId !== me.id) {
|
if (renote.userId !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exists({
|
const blockExist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: renote.userId,
|
blockerId: renote.userId,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
|
@ -349,7 +349,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (reply.userId !== me.id) {
|
if (reply.userId !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exists({
|
const blockExist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: reply.userId,
|
blockerId: reply.userId,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
// if already favorited
|
// if already favorited
|
||||||
const exist = await this.noteFavoritesRepository.exists({
|
const exist = await this.noteFavoritesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
// if already liked
|
// if already liked
|
||||||
const exist = await this.pageLikesRepository.exists({
|
const exist = await this.pageLikesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
pageId: page.id,
|
pageId: page.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const exist = await this.promoReadsRepository.exists({
|
const exist = await this.promoReadsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
|
|
@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (me == null) {
|
if (me == null) {
|
||||||
throw new ApiError(meta.errors.forbidden);
|
throw new ApiError(meta.errors.forbidden);
|
||||||
} else if (me.id !== user.id) {
|
} else if (me.id !== user.id) {
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followeeId: user.id,
|
followeeId: user.id,
|
||||||
followerId: me.id,
|
followerId: me.id,
|
||||||
|
|
|
@ -109,7 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (me == null) {
|
if (me == null) {
|
||||||
throw new ApiError(meta.errors.forbidden);
|
throw new ApiError(meta.errors.forbidden);
|
||||||
} else if (me.id !== user.id) {
|
} else if (me.id !== user.id) {
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
followeeId: user.id,
|
followeeId: user.id,
|
||||||
followerId: me.id,
|
followerId: me.id,
|
||||||
|
|
|
@ -90,7 +90,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private roleService: RoleService,
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const listExist = await this.userListsRepository.exists({
|
const listExist = await this.userListsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
id: ps.listId,
|
id: ps.listId,
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
|
@ -121,7 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
if (currentUser.id !== me.id) {
|
if (currentUser.id !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exists({
|
const blockExist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: currentUser.id,
|
blockerId: currentUser.id,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
|
@ -132,7 +132,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.userListMembershipsRepository.exists({
|
const exist = await this.userListMembershipsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userListId: userList.id,
|
userListId: userList.id,
|
||||||
userId: currentUser.id,
|
userId: currentUser.id,
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const userListExist = await this.userListsRepository.exists({
|
const userListExist = await this.userListsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
id: ps.listId,
|
id: ps.listId,
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
|
@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
throw new ApiError(meta.errors.noSuchList);
|
throw new ApiError(meta.errors.noSuchList);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.userListFavoritesRepository.exists({
|
const exist = await this.userListFavoritesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
userListId: ps.listId,
|
userListId: ps.listId,
|
||||||
|
|
|
@ -104,7 +104,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (user.id !== me.id) {
|
if (user.id !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exists({
|
const blockExist = await this.blockingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
blockerId: user.id,
|
blockerId: user.id,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
|
@ -115,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.userListMembershipsRepository.exists({
|
const exist = await this.userListMembershipsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userListId: userList.id,
|
userListId: userList.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
|
|
@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
userListId: ps.listId,
|
userListId: ps.listId,
|
||||||
});
|
});
|
||||||
if (me !== null) {
|
if (me !== null) {
|
||||||
additionalProperties.isLiked = await this.userListFavoritesRepository.exists({
|
additionalProperties.isLiked = await this.userListFavoritesRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
userListId: ps.listId,
|
userListId: ps.listId,
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
private userListFavoritesRepository: UserListFavoritesRepository,
|
private userListFavoritesRepository: UserListFavoritesRepository,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const userListExist = await this.userListsRepository.exists({
|
const userListExist = await this.userListsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
id: ps.listId,
|
id: ps.listId,
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
|
|
|
@ -43,7 +43,7 @@ class UserListChannel extends Channel {
|
||||||
this.withRenotes = params.withRenotes ?? true;
|
this.withRenotes = params.withRenotes ?? true;
|
||||||
|
|
||||||
// Check existence and owner
|
// Check existence and owner
|
||||||
const listExist = await this.userListsRepository.exists({
|
const listExist = await this.userListsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
id: this.listId,
|
id: this.listId,
|
||||||
userId: this.user!.id,
|
userId: this.user!.id,
|
||||||
|
|
|
@ -16,14 +16,12 @@ describe('Note', () => {
|
||||||
|
|
||||||
let alice: misskey.entities.SignupResponse;
|
let alice: misskey.entities.SignupResponse;
|
||||||
let bob: misskey.entities.SignupResponse;
|
let bob: misskey.entities.SignupResponse;
|
||||||
let tom: misskey.entities.SignupResponse;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const connection = await initTestDb(true);
|
const connection = await initTestDb(true);
|
||||||
Notes = connection.getRepository(MiNote);
|
Notes = connection.getRepository(MiNote);
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
bob = await signup({ username: 'bob' });
|
bob = await signup({ username: 'bob' });
|
||||||
tom = await signup({ username: 'tom', host: 'example.com' });
|
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
test('投稿できる', async () => {
|
test('投稿できる', async () => {
|
||||||
|
@ -609,77 +607,6 @@ describe('Note', () => {
|
||||||
assert.strictEqual(note2.status, 200);
|
assert.strictEqual(note2.status, 200);
|
||||||
assert.strictEqual(note2.body.createdNote.visibility, 'home');
|
assert.strictEqual(note2.body.createdNote.visibility, 'home');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('禁止ワードを含む投稿はエラーになる (単語指定)', async () => {
|
|
||||||
const prohibited = await api('admin/update-meta', {
|
|
||||||
prohibitedWords: [
|
|
||||||
'test',
|
|
||||||
],
|
|
||||||
}, alice);
|
|
||||||
|
|
||||||
assert.strictEqual(prohibited.status, 204);
|
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
|
||||||
|
|
||||||
const note1 = await api('/notes/create', {
|
|
||||||
text: 'hogetesthuge',
|
|
||||||
}, alice);
|
|
||||||
|
|
||||||
assert.strictEqual(note1.status, 400);
|
|
||||||
assert.strictEqual(note1.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('禁止ワードを含む投稿はエラーになる (正規表現)', async () => {
|
|
||||||
const prohibited = await api('admin/update-meta', {
|
|
||||||
prohibitedWords: [
|
|
||||||
'/Test/i',
|
|
||||||
],
|
|
||||||
}, alice);
|
|
||||||
|
|
||||||
assert.strictEqual(prohibited.status, 204);
|
|
||||||
|
|
||||||
const note2 = await api('/notes/create', {
|
|
||||||
text: 'hogetesthuge',
|
|
||||||
}, alice);
|
|
||||||
|
|
||||||
assert.strictEqual(note2.status, 400);
|
|
||||||
assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('禁止ワードを含む投稿はエラーになる (スペースアンド)', async () => {
|
|
||||||
const prohibited = await api('admin/update-meta', {
|
|
||||||
prohibitedWords: [
|
|
||||||
'Test hoge',
|
|
||||||
],
|
|
||||||
}, alice);
|
|
||||||
|
|
||||||
assert.strictEqual(prohibited.status, 204);
|
|
||||||
|
|
||||||
const note2 = await api('/notes/create', {
|
|
||||||
text: 'hogeTesthuge',
|
|
||||||
}, alice);
|
|
||||||
|
|
||||||
assert.strictEqual(note2.status, 400);
|
|
||||||
assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('禁止ワードを含んでいてもリモートノートはエラーにならない', async () => {
|
|
||||||
const prohibited = await api('admin/update-meta', {
|
|
||||||
prohibitedWords: [
|
|
||||||
'test',
|
|
||||||
],
|
|
||||||
}, alice);
|
|
||||||
|
|
||||||
assert.strictEqual(prohibited.status, 204);
|
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
|
||||||
|
|
||||||
const note1 = await api('/notes/create', {
|
|
||||||
text: 'hogetesthuge',
|
|
||||||
}, tom);
|
|
||||||
|
|
||||||
assert.strictEqual(note1.status, 200);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('notes/delete', () => {
|
describe('notes/delete', () => {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type SharedOptions, http, HttpResponse } from 'msw';
|
import { type SharedOptions, rest } from 'msw';
|
||||||
|
|
||||||
export const onUnhandledRequest = ((req, print) => {
|
export const onUnhandledRequest = ((req, print) => {
|
||||||
if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) {
|
if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) {
|
||||||
|
@ -13,31 +13,19 @@ export const onUnhandledRequest = ((req, print) => {
|
||||||
}) satisfies SharedOptions['onUnhandledRequest'];
|
}) satisfies SharedOptions['onUnhandledRequest'];
|
||||||
|
|
||||||
export const commonHandlers = [
|
export const commonHandlers = [
|
||||||
http.get('/fluent-emoji/:codepoints.png', async ({ params }) => {
|
rest.get('/fluent-emoji/:codepoints.png', async (req, res, ctx) => {
|
||||||
const { codepoints } = params;
|
const { codepoints } = req.params;
|
||||||
const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
||||||
return new HttpResponse(value, {
|
return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
|
||||||
headers: {
|
|
||||||
'Content-Type': 'image/png',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
http.get('/fluent-emojis/:codepoints.png', async ({ params }) => {
|
rest.get('/fluent-emojis/:codepoints.png', async (req, res, ctx) => {
|
||||||
const { codepoints } = params;
|
const { codepoints } = req.params;
|
||||||
const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
||||||
return new HttpResponse(value, {
|
return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
|
||||||
headers: {
|
|
||||||
'Content-Type': 'image/png',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
http.get('/twemoji/:codepoints.svg', async ({ params }) => {
|
rest.get('/twemoji/:codepoints.svg', async (req, res, ctx) => {
|
||||||
const { codepoints } = params;
|
const { codepoints } = req.params;
|
||||||
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
|
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
|
||||||
return new HttpResponse(value, {
|
return res(ctx.set('Content-Type', 'image/svg+xml'), ctx.body(value));
|
||||||
headers: {
|
|
||||||
'Content-Type': 'image/svg+xml',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"@twemoji/parser": "15.0.0",
|
"@twemoji/parser": "15.0.0",
|
||||||
"@vitejs/plugin-vue": "5.0.3",
|
"@vitejs/plugin-vue": "5.0.3",
|
||||||
"@vue/compiler-sfc": "3.4.15",
|
"@vue/compiler-sfc": "3.4.15",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
|
||||||
"astring": "1.8.6",
|
"astring": "1.8.6",
|
||||||
"broadcast-channel": "7.0.0",
|
"broadcast-channel": "7.0.0",
|
||||||
"buraha": "0.0.1",
|
"buraha": "0.0.1",
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
"typescript": "5.3.3",
|
"typescript": "5.3.3",
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"v-code-diff": "1.7.2",
|
"v-code-diff": "1.7.2",
|
||||||
"vite": "5.1.0",
|
"vite": "5.0.12",
|
||||||
"vue": "3.4.15",
|
"vue": "3.4.15",
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
},
|
},
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
"@types/estree": "1.0.5",
|
"@types/estree": "1.0.5",
|
||||||
"@types/matter-js": "0.19.6",
|
"@types/matter-js": "0.19.6",
|
||||||
"@types/micromatch": "4.0.6",
|
"@types/micromatch": "4.0.6",
|
||||||
"@types/node": "20.11.17",
|
"@types/node": "20.11.10",
|
||||||
"@types/punycode": "2.1.3",
|
"@types/punycode": "2.1.3",
|
||||||
"@types/sanitize-html": "2.9.5",
|
"@types/sanitize-html": "2.9.5",
|
||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
"@vue/runtime-core": "3.4.15",
|
"@vue/runtime-core": "3.4.15",
|
||||||
"acorn": "8.11.3",
|
"acorn": "8.11.3",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.6.4",
|
"cypress": "13.6.3",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-vue": "9.20.1",
|
"eslint-plugin-vue": "9.20.1",
|
||||||
|
@ -123,10 +123,10 @@
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "10.0.3",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.5",
|
"micromatch": "4.0.5",
|
||||||
"msw": "2.1.7",
|
"msw": "2.1.2",
|
||||||
"msw-storybook-addon": "2.0.0-beta.1",
|
"msw-storybook-addon": "1.10.0",
|
||||||
"nodemon": "3.0.3",
|
"nodemon": "3.0.3",
|
||||||
"prettier": "3.2.5",
|
"prettier": "3.2.4",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"start-server-and-test": "2.0.3",
|
"start-server-and-test": "2.0.3",
|
||||||
|
|
|
@ -60,6 +60,12 @@ export async function common(createVue: () => App<Element>) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const splash = document.getElementById('splash');
|
||||||
|
// 念のためnullチェック(HTMLが古い場合があるため(そのうち消す))
|
||||||
|
if (splash) splash.addEventListener('transitionend', () => {
|
||||||
|
splash.remove();
|
||||||
|
});
|
||||||
|
|
||||||
let isClientUpdated = false;
|
let isClientUpdated = false;
|
||||||
|
|
||||||
//#region クライアントが更新されたかチェック
|
//#region クライアントが更新されたかチェック
|
||||||
|
@ -287,10 +293,5 @@ function removeSplash() {
|
||||||
if (splash) {
|
if (splash) {
|
||||||
splash.style.opacity = '0';
|
splash.style.opacity = '0';
|
||||||
splash.style.pointerEvents = 'none';
|
splash.style.pointerEvents = 'none';
|
||||||
|
|
||||||
// transitionendイベントが発火しない場合があるため
|
|
||||||
window.setTimeout(() => {
|
|
||||||
splash.remove();
|
|
||||||
}, 1000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { abuseUserReport } from '../../.storybook/fakes.js';
|
import { abuseUserReport } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAbuseReport from './MkAbuseReport.vue';
|
import MkAbuseReport from './MkAbuseReport.vue';
|
||||||
|
@ -44,9 +44,9 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/admin/resolve-abuse-user-report', async ({ request }) => {
|
rest.post('/api/admin/resolve-abuse-user-report', async (req, res, ctx) => {
|
||||||
action('POST /api/admin/resolve-abuse-user-report')(await request.json());
|
action('POST /api/admin/resolve-abuse-user-report')(await req.json());
|
||||||
return HttpResponse.json({});
|
return res(ctx.json({}));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
|
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
|
||||||
|
@ -44,9 +44,9 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users/report-abuse', async ({ request }) => {
|
rest.post('/api/users/report-abuse', async (req, res, ctx) => {
|
||||||
action('POST /api/users/report-abuse')(await request.json());
|
action('POST /api/users/report-abuse')(await req.json());
|
||||||
return HttpResponse.json({});
|
return res(ctx.json({}));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAchievements from './MkAchievements.vue';
|
import MkAchievements from './MkAchievements.vue';
|
||||||
|
@ -39,8 +39,8 @@ export const Empty = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users/achievements', () => {
|
rest.post('/api/users/achievements', (req, res, ctx) => {
|
||||||
return HttpResponse.json([]);
|
return res(ctx.json([]));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -52,8 +52,8 @@ export const All = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users/achievements', () => {
|
rest.post('/api/users/achievements', (req, res, ctx) => {
|
||||||
return HttpResponse.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 })));
|
return res(ctx.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 }))));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { action } from '@storybook/addon-actions';
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAutocomplete from './MkAutocomplete.vue';
|
import MkAutocomplete from './MkAutocomplete.vue';
|
||||||
|
@ -99,11 +99,11 @@ export const User = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users/search-by-username-and-host', () => {
|
rest.post('/api/users/search-by-username-and-host', (req, res, ctx) => {
|
||||||
return HttpResponse.json([
|
return res(ctx.json([
|
||||||
userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
|
userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
|
||||||
userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
|
userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
|
||||||
]);
|
]));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -132,12 +132,12 @@ export const Hashtag = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/hashtags/search', () => {
|
rest.post('/api/hashtags/search', (req, res, ctx) => {
|
||||||
return HttpResponse.json([
|
return res(ctx.json([
|
||||||
'気象警報注意報',
|
'気象警報注意報',
|
||||||
'気象警報',
|
'気象警報',
|
||||||
'気象情報',
|
'気象情報',
|
||||||
]);
|
]));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAvatars from './MkAvatars.vue';
|
import MkAvatars from './MkAvatars.vue';
|
||||||
|
@ -38,12 +38,12 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users/show', () => {
|
rest.post('/api/users/show', (req, res, ctx) => {
|
||||||
return HttpResponse.json([
|
return res(ctx.json([
|
||||||
userDetailed('17'),
|
userDetailed('17'),
|
||||||
userDetailed('20'),
|
userDetailed('20'),
|
||||||
userDetailed('18'),
|
userDetailed('18'),
|
||||||
]);
|
]));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed, inviteCode } from '../../.storybook/fakes.js';
|
import { userDetailed, inviteCode } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkInviteCode from './MkInviteCode.vue';
|
import MkInviteCode from './MkInviteCode.vue';
|
||||||
|
@ -39,8 +39,8 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users/show', ({ params }) => {
|
rest.post('/api/users/show', (req, res, ctx) => {
|
||||||
return HttpResponse.json(userDetailed(params.userId as string));
|
return res(ctx.json(userDetailed(req.params.userId as string)));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue';
|
import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { ChannelConnection as Connection } from 'misskey-js';
|
||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
|
@ -89,8 +90,8 @@ function prepend(note) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let connection: Misskey.ChannelConnection | null = null;
|
let connection: Connection;
|
||||||
let connection2: Misskey.ChannelConnection | null = null;
|
let connection2: Connection;
|
||||||
let paginationQuery: Paging | null = null;
|
let paginationQuery: Paging | null = null;
|
||||||
|
|
||||||
const stream = useStream();
|
const stream = useStream();
|
||||||
|
@ -162,7 +163,7 @@ function connectChannel() {
|
||||||
roleId: props.role,
|
roleId: props.role,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (props.src !== 'directs' && props.src !== 'mentions') connection?.on('note', prepend);
|
if (props.src !== 'directs' && props.src !== 'mentions') connection.on('note', prepend);
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnectChannel() {
|
function disconnectChannel() {
|
||||||
|
|
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-if="player.url.startsWith('http://') || player.url.startsWith('https://')"
|
v-if="player.url.startsWith('http://') || player.url.startsWith('https://')"
|
||||||
sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin"
|
sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin"
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
:allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')"
|
:allow="player.allow.join(';')"
|
||||||
:class="$style.playerIframe"
|
:class="$style.playerIframe"
|
||||||
:src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')"
|
:src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')"
|
||||||
:style="{ border: 0 }"
|
:style="{ border: 0 }"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
|
import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
|
||||||
|
@ -38,17 +38,17 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users', () => {
|
rest.post('/api/users', (req, res, ctx) => {
|
||||||
return HttpResponse.json([
|
return res(ctx.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]);
|
]));
|
||||||
}),
|
}),
|
||||||
http.post('/api/pinned-users', () => {
|
rest.post('/api/pinned-users', (req, res, ctx) => {
|
||||||
return HttpResponse.json([
|
return res(ctx.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]);
|
]));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as Misskey from 'misskey-js';
|
import Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkUserSetupDialog from './MkUserSetupDialog.vue';
|
import MkUserSetupDialog from './MkUserSetupDialog.vue';
|
||||||
|
@ -38,17 +38,17 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users', () => {
|
rest.post('/api/users', (req, res, ctx) => {
|
||||||
return HttpResponse.json([
|
return res(ctx.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]);
|
]));
|
||||||
}),
|
}),
|
||||||
http.post('/api/pinned-users', () => {
|
rest.post('/api/pinned-users', (req, res, ctx) => {
|
||||||
return HttpResponse.json([
|
return res(ctx.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]);
|
]));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks.js';
|
import { commonHandlers } from '../../../.storybook/mocks.js';
|
||||||
import MkUrl from './MkUrl.vue';
|
import MkUrl from './MkUrl.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
|
@ -59,8 +59,8 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.get('/url', () => {
|
rest.get('/url', (req, res, ctx) => {
|
||||||
return HttpResponse.json({
|
return res(ctx.json({
|
||||||
title: 'Misskey Hub',
|
title: 'Misskey Hub',
|
||||||
icon: 'https://misskey-hub.net/favicon.ico',
|
icon: 'https://misskey-hub.net/favicon.ico',
|
||||||
description: 'Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。',
|
description: 'Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。',
|
||||||
|
@ -74,7 +74,7 @@ export const Default = {
|
||||||
sitename: 'misskey-hub.net',
|
sitename: 'misskey-hub.net',
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
url: 'https://misskey-hub.net/',
|
url: 'https://misskey-hub.net/',
|
||||||
});
|
}));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -49,11 +49,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
|
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
|
|
||||||
<MkTextarea v-model="prohibitedWords">
|
|
||||||
<template #label>{{ i18n.ts.prohibitedWords }}</template>
|
|
||||||
<template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
|
||||||
</MkTextarea>
|
|
||||||
|
|
||||||
<MkTextarea v-model="hiddenTags">
|
<MkTextarea v-model="hiddenTags">
|
||||||
<template #label>{{ i18n.ts.hiddenTags }}</template>
|
<template #label>{{ i18n.ts.hiddenTags }}</template>
|
||||||
<template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
|
<template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
|
||||||
|
@ -92,7 +87,6 @@ const emailRequiredForSignup = ref<boolean>(false);
|
||||||
const approvalRequiredForSignup = ref<boolean>(false);
|
const approvalRequiredForSignup = ref<boolean>(false);
|
||||||
const bubbleTimelineEnabled = ref<boolean>(false);
|
const bubbleTimelineEnabled = ref<boolean>(false);
|
||||||
const sensitiveWords = ref<string>('');
|
const sensitiveWords = ref<string>('');
|
||||||
const prohibitedWords = ref<string>('');
|
|
||||||
const hiddenTags = ref<string>('');
|
const hiddenTags = ref<string>('');
|
||||||
const preservedUsernames = ref<string>('');
|
const preservedUsernames = ref<string>('');
|
||||||
const bubbleTimeline = ref<string>('');
|
const bubbleTimeline = ref<string>('');
|
||||||
|
@ -105,7 +99,6 @@ async function init() {
|
||||||
emailRequiredForSignup.value = meta.emailRequiredForSignup;
|
emailRequiredForSignup.value = meta.emailRequiredForSignup;
|
||||||
approvalRequiredForSignup.value = meta.approvalRequiredForSignup;
|
approvalRequiredForSignup.value = meta.approvalRequiredForSignup;
|
||||||
sensitiveWords.value = meta.sensitiveWords.join('\n');
|
sensitiveWords.value = meta.sensitiveWords.join('\n');
|
||||||
prohibitedWords.value = meta.prohibitedWords.join('\n');
|
|
||||||
hiddenTags.value = meta.hiddenTags.join('\n');
|
hiddenTags.value = meta.hiddenTags.join('\n');
|
||||||
preservedUsernames.value = meta.preservedUsernames.join('\n');
|
preservedUsernames.value = meta.preservedUsernames.join('\n');
|
||||||
tosUrl.value = meta.tosUrl;
|
tosUrl.value = meta.tosUrl;
|
||||||
|
@ -122,7 +115,6 @@ function save() {
|
||||||
tosUrl: tosUrl.value,
|
tosUrl: tosUrl.value,
|
||||||
privacyPolicyUrl: privacyPolicyUrl.value,
|
privacyPolicyUrl: privacyPolicyUrl.value,
|
||||||
sensitiveWords: sensitiveWords.value.split('\n'),
|
sensitiveWords: sensitiveWords.value.split('\n'),
|
||||||
prohibitedWords: prohibitedWords.value.split('\n'),
|
|
||||||
hiddenTags: hiddenTags.value.split('\n'),
|
hiddenTags: hiddenTags.value.split('\n'),
|
||||||
preservedUsernames: preservedUsernames.value.split('\n'),
|
preservedUsernames: preservedUsernames.value.split('\n'),
|
||||||
bubbleInstances: bubbleTimeline.value.split('\n'),
|
bubbleInstances: bubbleTimeline.value.split('\n'),
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../../.storybook/fakes.js';
|
import { userDetailed } from '../../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks.js';
|
import { commonHandlers } from '../../../.storybook/mocks.js';
|
||||||
import home_ from './home.vue';
|
import home_ from './home.vue';
|
||||||
|
@ -39,13 +39,12 @@ export const Default = {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
http.post('/api/users/notes', () => {
|
rest.post('/api/users/notes', (req, res, ctx) => {
|
||||||
return HttpResponse.json([]);
|
return res(ctx.json([]));
|
||||||
}),
|
}),
|
||||||
http.get('/api/charts/user/notes', ({ request }) => {
|
rest.get('/api/charts/user/notes', (req, res, ctx) => {
|
||||||
const url = new URL(request.url);
|
const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
||||||
const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
return res(ctx.json({
|
||||||
return HttpResponse.json({
|
|
||||||
total: Array.from({ length }, () => 0),
|
total: Array.from({ length }, () => 0),
|
||||||
inc: Array.from({ length }, () => 0),
|
inc: Array.from({ length }, () => 0),
|
||||||
dec: Array.from({ length }, () => 0),
|
dec: Array.from({ length }, () => 0),
|
||||||
|
@ -55,12 +54,11 @@ export const Default = {
|
||||||
renote: Array.from({ length }, () => 0),
|
renote: Array.from({ length }, () => 0),
|
||||||
withFile: Array.from({ length }, () => 0),
|
withFile: Array.from({ length }, () => 0),
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
}),
|
}),
|
||||||
http.get('/api/charts/user/pv', ({ request }) => {
|
rest.get('/api/charts/user/pv', (req, res, ctx) => {
|
||||||
const url = new URL(request.url);
|
const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
||||||
const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
return res(ctx.json({
|
||||||
return HttpResponse.json({
|
|
||||||
upv: {
|
upv: {
|
||||||
user: Array.from({ length }, () => 0),
|
user: Array.from({ length }, () => 0),
|
||||||
visitor: Array.from({ length }, () => 0),
|
visitor: Array.from({ length }, () => 0),
|
||||||
|
@ -69,7 +67,7 @@ export const Default = {
|
||||||
user: Array.from({ length }, () => 0),
|
user: Array.from({ length }, () => 0),
|
||||||
visitor: Array.from({ length }, () => 0),
|
visitor: Array.from({ length }, () => 0),
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -68,7 +68,10 @@ export async function initHighlighter() {
|
||||||
themes,
|
themes,
|
||||||
langs: [
|
langs: [
|
||||||
import('shiki/langs/javascript.mjs'),
|
import('shiki/langs/javascript.mjs'),
|
||||||
aiScriptGrammar.default as unknown as LanguageRegistration,
|
{
|
||||||
|
aliases: ['is', 'ais'],
|
||||||
|
...aiScriptGrammar.default,
|
||||||
|
} as unknown as LanguageRegistration,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -116,34 +116,6 @@ describe('MkUrlPreview', () => {
|
||||||
assert.strictEqual(iframe?.allow, 'fullscreen;web-share');
|
assert.strictEqual(iframe?.allow, 'fullscreen;web-share');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('A Summaly proxy response without allow falls back to the default', async () => {
|
|
||||||
const iframe = await renderAndOpenPreview({
|
|
||||||
url: 'https://example.local',
|
|
||||||
player: {
|
|
||||||
url: 'https://example.local/player',
|
|
||||||
width: null,
|
|
||||||
height: null,
|
|
||||||
allow: undefined as any,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
assert.exists(iframe, 'iframe should exist');
|
|
||||||
assert.strictEqual(iframe?.allow, 'autoplay;encrypted-media;fullscreen');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Filtering the allow list from the Summaly proxy', async () => {
|
|
||||||
const iframe = await renderAndOpenPreview({
|
|
||||||
url: 'https://example.local',
|
|
||||||
player: {
|
|
||||||
url: 'https://example.local/player',
|
|
||||||
width: null,
|
|
||||||
height: null,
|
|
||||||
allow: ['autoplay', 'camera', 'fullscreen'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
assert.exists(iframe, 'iframe should exist');
|
|
||||||
assert.strictEqual(iframe?.allow, 'autoplay;fullscreen');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Having a player width should keep the fixed aspect ratio', async () => {
|
test('Having a player width should keep the fixed aspect ratio', async () => {
|
||||||
const iframe = await renderAndOpenPreview({
|
const iframe = await renderAndOpenPreview({
|
||||||
url: 'https://example.local',
|
url: 'https://example.local',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2024.2.0-beta.11",
|
"version": "2024.2.0-beta.10",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"types": "./built/dts/index.d.ts",
|
"types": "./built/dts/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@swc/jest": "0.2.31",
|
"@swc/jest": "0.2.31",
|
||||||
"@types/jest": "29.5.11",
|
"@types/jest": "29.5.11",
|
||||||
"@types/node": "20.11.17",
|
"@types/node": "20.11.10",
|
||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
|
|
|
@ -4793,7 +4793,6 @@ export type operations = {
|
||||||
hiddenTags: string[];
|
hiddenTags: string[];
|
||||||
blockedHosts: string[];
|
blockedHosts: string[];
|
||||||
sensitiveWords: string[];
|
sensitiveWords: string[];
|
||||||
prohibitedWords: string[];
|
|
||||||
bannedEmailDomains?: string[];
|
bannedEmailDomains?: string[];
|
||||||
preservedUsernames: string[];
|
preservedUsernames: string[];
|
||||||
bubbleInstances: string[];
|
bubbleInstances: string[];
|
||||||
|
@ -8815,7 +8814,6 @@ export type operations = {
|
||||||
hiddenTags?: string[] | null;
|
hiddenTags?: string[] | null;
|
||||||
blockedHosts?: string[] | null;
|
blockedHosts?: string[] | null;
|
||||||
sensitiveWords?: string[] | null;
|
sensitiveWords?: string[] | null;
|
||||||
prohibitedWords?: string[] | null;
|
|
||||||
themeColor?: string | null;
|
themeColor?: string | null;
|
||||||
mascotImageUrl?: string | null;
|
mascotImageUrl?: string | null;
|
||||||
bannerUrl?: string | null;
|
bannerUrl?: string | null;
|
||||||
|
|
580
pnpm-lock.yaml
580
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue