Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop
This commit is contained in:
		
						commit
						aba06b4ef9
					
				
					 26 changed files with 92 additions and 213 deletions
				
			
		|  | @ -21,6 +21,12 @@ You should also include the user name that made the change. | ||||||
| - 新たに動的なPagesを作ることはできなくなりました | - 新たに動的なPagesを作ることはできなくなりました | ||||||
| 	- 代わりに今後AiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能の実装を予定しています。 | 	- 代わりに今後AiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能の実装を予定しています。 | ||||||
| - iOS15以下のデバイスはサポートされなくなりました | - iOS15以下のデバイスはサポートされなくなりました | ||||||
|  | - API: カスタム絵文字エンティティに`url`プロパティが含まれなくなりました | ||||||
|  | 	- 絵文字画像を表示するには、`<instance host>/emoji/<emoji name>.webp`にリクエストすると画像が返ります。 | ||||||
|  | 	- e.g. `https://p1.a9z.dev/emoji/misskey.webp` | ||||||
|  | 	- remote: `https://p1.a9z.dev/emoji/syuilo_birth_present@mk.f72u.net.webp` | ||||||
|  | - API: `user`および`note`エンティティに`emojis`プロパティが含まれなくなりました | ||||||
|  | - API: `user`エンティティに`avatarColor`および`bannerColor`プロパティが含まれなくなりました | ||||||
| 
 | 
 | ||||||
| ### Improvements | ### Improvements | ||||||
| - Push notification of Antenna note @tamaina | - Push notification of Antenna note @tamaina | ||||||
|  |  | ||||||
|  | @ -914,6 +914,8 @@ windowMaximize: "Maximieren" | ||||||
| windowRestore: "Wiederherstellen" | windowRestore: "Wiederherstellen" | ||||||
| caption: "Beschreibung" | caption: "Beschreibung" | ||||||
| loggedInAsBot: "Momentan als Bot angemeldet" | loggedInAsBot: "Momentan als Bot angemeldet" | ||||||
|  | tools: "Werkzeuge" | ||||||
|  | cannotLoad: "Kann nicht geladen werden" | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht." |   description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht." | ||||||
|   sensitivity: "Erkennungssensitivität" |   sensitivity: "Erkennungssensitivität" | ||||||
|  |  | ||||||
|  | @ -914,6 +914,8 @@ windowMaximize: "Maximize" | ||||||
| windowRestore: "Restore" | windowRestore: "Restore" | ||||||
| caption: "Caption" | caption: "Caption" | ||||||
| loggedInAsBot: "Currently logged in as bot" | loggedInAsBot: "Currently logged in as bot" | ||||||
|  | tools: "Tools" | ||||||
|  | cannotLoad: "Unable to load" | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." |   description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." | ||||||
|   sensitivity: "Detection sensitivity" |   sensitivity: "Detection sensitivity" | ||||||
|  |  | ||||||
|  | @ -49,6 +49,7 @@ deleteAndEdit: "ほかして直す" | ||||||
| deleteAndEditConfirm: "このノートをほかして書き直すんか?このノートへのリアクション、Renote、返信も全部消えてまうで。" | deleteAndEditConfirm: "このノートをほかして書き直すんか?このノートへのリアクション、Renote、返信も全部消えてまうで。" | ||||||
| addToList: "リストに入れたる" | addToList: "リストに入れたる" | ||||||
| sendMessage: "メッセージを送る" | sendMessage: "メッセージを送る" | ||||||
|  | copyRSS: "RSSをコピー" | ||||||
| copyUsername: "ユーザー名をコピー" | copyUsername: "ユーザー名をコピー" | ||||||
| searchUser: "ユーザーを検索" | searchUser: "ユーザーを検索" | ||||||
| reply: "返事" | reply: "返事" | ||||||
|  | @ -456,6 +457,8 @@ language: "言語" | ||||||
| uiLanguage: "UIの表示言語" | uiLanguage: "UIの表示言語" | ||||||
| groupInvited: "グループに招待されとるで" | groupInvited: "グループに招待されとるで" | ||||||
| aboutX: "{x}について" | aboutX: "{x}について" | ||||||
|  | emojiStyle: "絵文字のスタイル" | ||||||
|  | native: "ネイティブ" | ||||||
| disableDrawer: "メニューをドロワーで表示せぇへん" | disableDrawer: "メニューをドロワーで表示せぇへん" | ||||||
| youHaveNoGroups: "グループがあらへんねぇ。" | youHaveNoGroups: "グループがあらへんねぇ。" | ||||||
| joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループ作ってからやってな" | joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループ作ってからやってな" | ||||||
|  | @ -714,6 +717,7 @@ accentColor: "アクセント" | ||||||
| textColor: "文字" | textColor: "文字" | ||||||
| saveAs: "名前を付けて保存" | saveAs: "名前を付けて保存" | ||||||
| advanced: "高度" | advanced: "高度" | ||||||
|  | advancedSettings: "高度な設定" | ||||||
| value: "値" | value: "値" | ||||||
| createdAt: "作成した日" | createdAt: "作成した日" | ||||||
| updatedAt: "更新日時" | updatedAt: "更新日時" | ||||||
|  | @ -910,6 +914,8 @@ windowMaximize: "最大化" | ||||||
| windowRestore: "元に戻す" | windowRestore: "元に戻す" | ||||||
| caption: "キャプション" | caption: "キャプション" | ||||||
| loggedInAsBot: "Botアカウントでログイン中やで" | loggedInAsBot: "Botアカウントでログイン中やで" | ||||||
|  | tools: "ツール" | ||||||
|  | cannotLoad: "読み込めへんで" | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。" |   description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。" | ||||||
|   sensitivity: "検出感度やで" |   sensitivity: "検出感度やで" | ||||||
|  | @ -1310,6 +1316,7 @@ _widgets: | ||||||
|   serverMetric: "サーバーメトリクス" |   serverMetric: "サーバーメトリクス" | ||||||
|   aiscript: "AiScriptコンソール" |   aiscript: "AiScriptコンソール" | ||||||
|   aichan: "藍" |   aichan: "藍" | ||||||
|  |   userList: "ユーザーリスト" | ||||||
|   _userList: |   _userList: | ||||||
|     chooseList: "リストを選ぶ" |     chooseList: "リストを選ぶ" | ||||||
| _cw: | _cw: | ||||||
|  |  | ||||||
|  | @ -717,6 +717,7 @@ accentColor: "강조 색상" | ||||||
| textColor: "문자 색" | textColor: "문자 색" | ||||||
| saveAs: "다른 이름으로 저장" | saveAs: "다른 이름으로 저장" | ||||||
| advanced: "고급" | advanced: "고급" | ||||||
|  | advancedSettings: "고급 설정" | ||||||
| value: "값" | value: "값" | ||||||
| createdAt: "생성된 날짜" | createdAt: "생성된 날짜" | ||||||
| updatedAt: "수정한 날짜" | updatedAt: "수정한 날짜" | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| _lang_: "Polski" | _lang_: "Polski" | ||||||
| headlineMisskey: "Sieć połączona wpisami" | headlineMisskey: "Sieć połączona wpisami" | ||||||
| introMisskey: "Misskey jest serwisem mikroblogowym typu open source.\nMisskey to opensource'owy serwis mikroblogowy, w którym możesz tworzyć \"notatki\", aby dzielić się tym, co się dzieje i opowiadać wszystkim o sobie.\nMożesz również użyć funkcji \"Reakcje\", aby szybko dodać własne reakcje do notatek innych użytkowników👍.\nOdkrywaj nowy świat🚀!" | introMisskey: "Misskey jest serwisem mikroblogowym typu open source.\nMisskey to opensource'owy serwis mikroblogowy, w którym możesz tworzyć \"notatki\", aby dzielić się tym, co się dzieje i opowiadać wszystkim o sobie.\nMożesz również użyć funkcji \"Reakcje\", aby szybko dodać własne reakcje do notatek innych użytkowników👍.\nOdkrywaj nowy świat🚀!" | ||||||
|  | poweredByMisskeyDescription: "{name} jest jedną z usług działającą na otwartoźródłowej platformie <b>Misskey</b> (określana jako \"instancja Misskey\")." | ||||||
| monthAndDay: "{month}-{day}" | monthAndDay: "{month}-{day}" | ||||||
| search: "Szukaj" | search: "Szukaj" | ||||||
| notifications: "Powiadomienia" | notifications: "Powiadomienia" | ||||||
|  | @ -48,6 +49,7 @@ deleteAndEdit: "Usuń i edytuj" | ||||||
| deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz wszystkie reakcje, udostępnienia i odpowiedzi do tego wpisu." | deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz wszystkie reakcje, udostępnienia i odpowiedzi do tego wpisu." | ||||||
| addToList: "Dodaj do listy" | addToList: "Dodaj do listy" | ||||||
| sendMessage: "Wyślij wiadomość" | sendMessage: "Wyślij wiadomość" | ||||||
|  | copyRSS: "Kopiuj RSS" | ||||||
| copyUsername: "Kopiuj nazwę użytkownika" | copyUsername: "Kopiuj nazwę użytkownika" | ||||||
| searchUser: "Wyszukiwanie użytkowników" | searchUser: "Wyszukiwanie użytkowników" | ||||||
| reply: "Odpowiedz" | reply: "Odpowiedz" | ||||||
|  | @ -346,6 +348,8 @@ recaptcha: "reCAPTCHA" | ||||||
| enableRecaptcha: "Włącz reCAPTCHA" | enableRecaptcha: "Włącz reCAPTCHA" | ||||||
| recaptchaSiteKey: "Klucz strony" | recaptchaSiteKey: "Klucz strony" | ||||||
| recaptchaSecretKey: "Tajny klucz" | recaptchaSecretKey: "Tajny klucz" | ||||||
|  | turnstile: "Turnstile" | ||||||
|  | enableTurnstile: "Włącz Turnstile" | ||||||
| turnstileSiteKey: "Klucz strony" | turnstileSiteKey: "Klucz strony" | ||||||
| turnstileSecretKey: "Tajny klucz" | turnstileSecretKey: "Tajny klucz" | ||||||
| avoidMultiCaptchaConfirm: "Używanie wielu Captchy może spowodować zakłócenia. Czy chcesz wyłączyć inną Captchę? Możesz zostawić wiele jednocześnie, klikając Anuluj." | avoidMultiCaptchaConfirm: "Używanie wielu Captchy może spowodować zakłócenia. Czy chcesz wyłączyć inną Captchę? Możesz zostawić wiele jednocześnie, klikając Anuluj." | ||||||
|  | @ -450,6 +454,8 @@ language: "Język" | ||||||
| uiLanguage: "Język wyświetlania UI" | uiLanguage: "Język wyświetlania UI" | ||||||
| groupInvited: "Zaproszony(-a) do grupy" | groupInvited: "Zaproszony(-a) do grupy" | ||||||
| aboutX: "O {x}" | aboutX: "O {x}" | ||||||
|  | emojiStyle: "Styl emoji" | ||||||
|  | native: "Natywny" | ||||||
| disableDrawer: "Nie używaj menu w stylu szuflady" | disableDrawer: "Nie używaj menu w stylu szuflady" | ||||||
| youHaveNoGroups: "Nie masz żadnych grup" | youHaveNoGroups: "Nie masz żadnych grup" | ||||||
| joinOrCreateGroup: "Uzyskaj zaproszenie do dołączenia do grupy lub utwórz własną grupę." | joinOrCreateGroup: "Uzyskaj zaproszenie do dołączenia do grupy lub utwórz własną grupę." | ||||||
|  | @ -824,7 +830,16 @@ size: "Rozmiar" | ||||||
| numberOfColumn: "Liczba kolumn" | numberOfColumn: "Liczba kolumn" | ||||||
| searchByGoogle: "Szukaj" | searchByGoogle: "Szukaj" | ||||||
| indefinitely: "Nigdy" | indefinitely: "Nigdy" | ||||||
|  | tenMinutes: "10 minut" | ||||||
|  | oneHour: "1 godzina" | ||||||
|  | oneDay: "1 dzień" | ||||||
|  | oneWeek: "1 tydzień" | ||||||
| file: "Pliki" | file: "Pliki" | ||||||
|  | recommended: "Zalecane" | ||||||
|  | check: "Zweryfikuj" | ||||||
|  | deleteAccount: "Usuń konto" | ||||||
|  | document: "Dokumentacja" | ||||||
|  | numberOfPageCache: "Ilość stron w cache" | ||||||
| logoutConfirm: "Czy na pewno chcesz się wylogować?" | logoutConfirm: "Czy na pewno chcesz się wylogować?" | ||||||
| lastActiveDate: "Ostatnio użyte w" | lastActiveDate: "Ostatnio użyte w" | ||||||
| statusbar: "Pasek stanu" | statusbar: "Pasek stanu" | ||||||
|  |  | ||||||
|  | @ -49,6 +49,7 @@ deleteAndEdit: "Odstrániť a upraviť" | ||||||
| deleteAndEditConfirm: "Naozaj chcete odstrániť túto poznámku a upraviť ju? Stratíte tým všetky reakcie a odpovede na ňu." | deleteAndEditConfirm: "Naozaj chcete odstrániť túto poznámku a upraviť ju? Stratíte tým všetky reakcie a odpovede na ňu." | ||||||
| addToList: "Pridať do zoznamu" | addToList: "Pridať do zoznamu" | ||||||
| sendMessage: "Odoslať správu" | sendMessage: "Odoslať správu" | ||||||
|  | copyRSS: "Kopírovať RSS" | ||||||
| copyUsername: "Kopírovať meno používateľa" | copyUsername: "Kopírovať meno používateľa" | ||||||
| searchUser: "Hľadať používateľov" | searchUser: "Hľadať používateľov" | ||||||
| reply: "Odpovedať" | reply: "Odpovedať" | ||||||
|  | @ -456,6 +457,8 @@ language: "Jazyk" | ||||||
| uiLanguage: "Jazyk používateľského prostredia" | uiLanguage: "Jazyk používateľského prostredia" | ||||||
| groupInvited: "Pozvať do skupiny" | groupInvited: "Pozvať do skupiny" | ||||||
| aboutX: "O {x}" | aboutX: "O {x}" | ||||||
|  | emojiStyle: "Štýl emoji" | ||||||
|  | native: "Natívne" | ||||||
| disableDrawer: "Nepoužívať šuflíkové menu" | disableDrawer: "Nepoužívať šuflíkové menu" | ||||||
| youHaveNoGroups: "Nemáte žiadne skupiny" | youHaveNoGroups: "Nemáte žiadne skupiny" | ||||||
| joinOrCreateGroup: "Požiadajte o pozvanie do existujúcej skupiny alebo vytvorte novú." | joinOrCreateGroup: "Požiadajte o pozvanie do existujúcej skupiny alebo vytvorte novú." | ||||||
|  | @ -713,6 +716,7 @@ accentColor: "Akcent" | ||||||
| textColor: "Text" | textColor: "Text" | ||||||
| saveAs: "Uložiť ako..." | saveAs: "Uložiť ako..." | ||||||
| advanced: "Rozšírené" | advanced: "Rozšírené" | ||||||
|  | advancedSettings: "Rozšírené nastavenia" | ||||||
| value: "Hodnoty" | value: "Hodnoty" | ||||||
| createdAt: "Vytvorené" | createdAt: "Vytvorené" | ||||||
| updatedAt: "Upravené" | updatedAt: "Upravené" | ||||||
|  | @ -906,6 +910,8 @@ sendPushNotificationReadMessageCaption: "Na chvíľu sa zobrazí oznámenie \"{e | ||||||
| windowMaximize: "Maximalizovať" | windowMaximize: "Maximalizovať" | ||||||
| windowRestore: "Obnoviť" | windowRestore: "Obnoviť" | ||||||
| caption: "Nadpis" | caption: "Nadpis" | ||||||
|  | tools: "Nástroje" | ||||||
|  | cannotLoad: "Nedá sa načítať." | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera." |   description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera." | ||||||
|   sensitivity: "Citlivosť detekcie" |   sensitivity: "Citlivosť detekcie" | ||||||
|  |  | ||||||
|  | @ -164,7 +164,7 @@ host: "โฮสต์" | ||||||
| selectUser: "เลือกผู้ใช้งาน" | selectUser: "เลือกผู้ใช้งาน" | ||||||
| recipient: "ผู้รับ" | recipient: "ผู้รับ" | ||||||
| annotation: "ความคิดเห็น" | annotation: "ความคิดเห็น" | ||||||
| federation: "สหพันธ์" | federation: "เฟดิเวิร์ส" | ||||||
| instances: "ตัวอย่าง" | instances: "ตัวอย่าง" | ||||||
| registeredAt: "จดทะเบียนที่" | registeredAt: "จดทะเบียนที่" | ||||||
| latestRequestSentAt: "ส่งคำขอล่าสุดไปแล้ว" | latestRequestSentAt: "ส่งคำขอล่าสุดไปแล้ว" | ||||||
|  | @ -228,10 +228,10 @@ newPassword: "รหัสผ่านใหม่" | ||||||
| newPasswordRetype: "ใส่รหัสผ่านใหม่อีกครั้ง" | newPasswordRetype: "ใส่รหัสผ่านใหม่อีกครั้ง" | ||||||
| attachFile: "แนบไฟล์" | attachFile: "แนบไฟล์" | ||||||
| more: "เพิ่มเติม!" | more: "เพิ่มเติม!" | ||||||
| featured: "เป็นจุดเด่น" | featured: "ไฮไลท์" | ||||||
| usernameOrUserId: "ชื่อผู้ใช้หรือรหัสผู้ใช้งาน" | usernameOrUserId: "ชื่อผู้ใช้หรือรหัสผู้ใช้งาน" | ||||||
| noSuchUser: "ไม่มีผู้ใช้นี้อยู่ในระบบ" | noSuchUser: "ไม่มีผู้ใช้นี้อยู่ในระบบ" | ||||||
| lookup: "ค้นหา" | lookup: "การค้นหา" | ||||||
| announcements: "ประกาศ" | announcements: "ประกาศ" | ||||||
| imageUrl: "url รูปภาพ" | imageUrl: "url รูปภาพ" | ||||||
| remove: "ลบ" | remove: "ลบ" | ||||||
|  | @ -914,6 +914,8 @@ windowMaximize: "ขยายใหญ่สุดแล้ว" | ||||||
| windowRestore: "เลิกทำ" | windowRestore: "เลิกทำ" | ||||||
| caption: "รายละเอียด" | caption: "รายละเอียด" | ||||||
| loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้" | loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้" | ||||||
|  | tools: "เครื่องมือ" | ||||||
|  | cannotLoad: "ไม่สามารถโหลดได้" | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย" |   description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย" | ||||||
|   sensitivity: "การตรวจจับความไว" |   sensitivity: "การตรวจจับความไว" | ||||||
|  |  | ||||||
|  | @ -1019,6 +1019,8 @@ _mfm: | ||||||
|   font: "Шрифт" |   font: "Шрифт" | ||||||
|   fontDescription: "Встановлює шрифт для контенту." |   fontDescription: "Встановлює шрифт для контенту." | ||||||
|   rotate: "Обертати" |   rotate: "Обертати" | ||||||
|  |   plain: "Звичайний" | ||||||
|  |   plainDescription: "Деактивує всі ефекти MFM, що містяться в цьому ефекті MFM." | ||||||
| _instanceTicker: | _instanceTicker: | ||||||
|   none: "Не відображати" |   none: "Не відображати" | ||||||
|   remote: "Відображати для віддалених користувачів" |   remote: "Відображати для віддалених користувачів" | ||||||
|  | @ -1053,6 +1055,7 @@ _wordMute: | ||||||
| _instanceMute: | _instanceMute: | ||||||
|   instanceMuteDescription2: "Розділяйте новими рядками" |   instanceMuteDescription2: "Розділяйте новими рядками" | ||||||
|   title: "Приховує нотатки з перелічених інстансів." |   title: "Приховує нотатки з перелічених інстансів." | ||||||
|  |   heading: "Список заглушених інстансів" | ||||||
| _theme: | _theme: | ||||||
|   explore: "Оглянути теми" |   explore: "Оглянути теми" | ||||||
|   install: "Встановити тему" |   install: "Встановити тему" | ||||||
|  | @ -1070,7 +1073,10 @@ _theme: | ||||||
|   color: "Колір" |   color: "Колір" | ||||||
|   key: "Ключ" |   key: "Ключ" | ||||||
|   func: "Функції" |   func: "Функції" | ||||||
|  |   funcKind: "Тип функції" | ||||||
|   argument: "Аргумент" |   argument: "Аргумент" | ||||||
|  |   alpha: "Непрозорість" | ||||||
|  |   darken: "Затемнення" | ||||||
|   lighten: "Яскравість" |   lighten: "Яскравість" | ||||||
|   inputConstantName: "Введіть назву константи" |   inputConstantName: "Введіть назву константи" | ||||||
|   importInfo: "Вставляючи сюди код теми, ви можете добавити її до редактору тем" |   importInfo: "Вставляючи сюди код теми, ви можете добавити її до редактору тем" | ||||||
|  | @ -1165,10 +1171,17 @@ _tutorial: | ||||||
|   step7_1: "Вітаю! Ви пройшли ознайомлення з Misskey." |   step7_1: "Вітаю! Ви пройшли ознайомлення з Misskey." | ||||||
|   step7_2: "Якщо ви хочете більше дізнатись про Misskey, зайдіть в розділ {help}." |   step7_2: "Якщо ви хочете більше дізнатись про Misskey, зайдіть в розділ {help}." | ||||||
|   step7_3: "Насолоджуйтесь Misskey! 🚀" |   step7_3: "Насолоджуйтесь Misskey! 🚀" | ||||||
|  |   step8_1: "Наостанку, чи бажаєте ви ввімкнути push-сповіщення?" | ||||||
|   step8_3: "Ви завжди можете змінити цей параметр пізніше." |   step8_3: "Ви завжди можете змінити цей параметр пізніше." | ||||||
| _2fa: | _2fa: | ||||||
|  |   alreadyRegistered: "Двофакторна автентифікація вже налаштована." | ||||||
|   registerDevice: "Зареєструвати новий пристрій" |   registerDevice: "Зареєструвати новий пристрій" | ||||||
|   registerKey: "Зареєструвати новий ключ безпеки" |   registerKey: "Зареєструвати новий ключ безпеки" | ||||||
|  |   step1: "Спершу встановіть на свій пристрій програму автентифікації (наприклад {a} або {b})." | ||||||
|  |   step2: "Потім відскануйте QR-код, який відображається на цьому екрані." | ||||||
|  |   step2Url: "Ви також можете ввести цю URL-адресу, якщо використовуєте програму для ПК:" | ||||||
|  |   step3: "Щоб завершити налаштування, введіть токен, наданий вашою програмою." | ||||||
|  |   step4: "Відтепер будь-які майбутні спроби входу вимагатимуть такого токена." | ||||||
| _permissions: | _permissions: | ||||||
|   "read:account": "Переглядати дані профілю" |   "read:account": "Переглядати дані профілю" | ||||||
|   "write:account": "Змінити дані акаунту" |   "write:account": "Змінити дані акаунту" | ||||||
|  | @ -1186,6 +1199,7 @@ _permissions: | ||||||
|   "write:mutes": "Змінювати список ігнорованих" |   "write:mutes": "Змінювати список ігнорованих" | ||||||
|   "write:notes": "Писати і видаляти нотатки" |   "write:notes": "Писати і видаляти нотатки" | ||||||
|   "read:notifications": "Переглядати сповіщення" |   "read:notifications": "Переглядати сповіщення" | ||||||
|  |   "write:notifications": "Керування сповіщеннями" | ||||||
|   "read:reactions": "Переглядати реакції" |   "read:reactions": "Переглядати реакції" | ||||||
|   "write:reactions": "Змінювати реакції" |   "write:reactions": "Змінювати реакції" | ||||||
|   "write:votes": "Голосувати в опитуваннях" |   "write:votes": "Голосувати в опитуваннях" | ||||||
|  | @ -1224,7 +1238,9 @@ _widgets: | ||||||
|   activity: "Активність" |   activity: "Активність" | ||||||
|   photos: "Фото" |   photos: "Фото" | ||||||
|   digitalClock: "Цифровий годинник" |   digitalClock: "Цифровий годинник" | ||||||
|  |   unixClock: "Unix-годинник" | ||||||
|   federation: "Федіверс" |   federation: "Федіверс" | ||||||
|  |   instanceCloud: "Хмара інстансів" | ||||||
|   postForm: "Створення нотатки" |   postForm: "Створення нотатки" | ||||||
|   slideshow: "Слайд-шоу" |   slideshow: "Слайд-шоу" | ||||||
|   button: "Кнопка" |   button: "Кнопка" | ||||||
|  | @ -1232,6 +1248,8 @@ _widgets: | ||||||
|   jobQueue: "Черга завдань" |   jobQueue: "Черга завдань" | ||||||
|   serverMetric: "Показники сервера " |   serverMetric: "Показники сервера " | ||||||
|   aiscript: "Консоль AiScript" |   aiscript: "Консоль AiScript" | ||||||
|  |   aichan: "Ai" | ||||||
|  |   userList: "Список користувачів" | ||||||
|   _userList: |   _userList: | ||||||
|     chooseList: "Виберіть список" |     chooseList: "Виберіть список" | ||||||
| _cw: | _cw: | ||||||
|  | @ -1301,16 +1319,23 @@ _exportOrImport: | ||||||
|   muteList: "Ігнорувати" |   muteList: "Ігнорувати" | ||||||
|   blockingList: "Заблокувати" |   blockingList: "Заблокувати" | ||||||
|   userLists: "Списки" |   userLists: "Списки" | ||||||
|  |   excludeMutingUsers: "Виключити ігнорованих користувачів" | ||||||
|  |   excludeInactiveUsers: "Виключити неактивних користувачів" | ||||||
| _charts: | _charts: | ||||||
|   federation: "Федіверс" |   federation: "Федіверс" | ||||||
|   apRequest: "Запити" |   apRequest: "Запити" | ||||||
|  |   usersIncDec: "Зміни кількості користувачів" | ||||||
|   usersTotal: "Загальна кількість користувачів" |   usersTotal: "Загальна кількість користувачів" | ||||||
|   activeUsers: "Активні користувачі" |   activeUsers: "Активні користувачі" | ||||||
|  |   notesIncDec: "Зміни кількості нотаток" | ||||||
|  |   localNotesIncDec: "Зміни кількості локальних нотаток" | ||||||
|  |   remoteNotesIncDec: "Зміни кількості віддалених нотаток" | ||||||
|   notesTotal: "Загальна кількість нотаток" |   notesTotal: "Загальна кількість нотаток" | ||||||
|   filesIncDec: "Зміни кількості файлів" |   filesIncDec: "Зміни кількості файлів" | ||||||
|   filesTotal: "Загальна кількість файлів" |   filesTotal: "Загальна кількість файлів" | ||||||
| _instanceCharts: | _instanceCharts: | ||||||
|   requests: "Запити" |   requests: "Запити" | ||||||
|  |   users: "Зміни кількості користувачів" | ||||||
|   usersTotal: "Сумарна кількість користувачів" |   usersTotal: "Сумарна кількість користувачів" | ||||||
|   notes: "Різниця кількості зроблених записів" |   notes: "Різниця кількості зроблених записів" | ||||||
|   notesTotal: "Сумарна кількість нотаток" |   notesTotal: "Сумарна кількість нотаток" | ||||||
|  |  | ||||||
|  | @ -914,6 +914,7 @@ windowMaximize: "最大化" | ||||||
| windowRestore: "还原" | windowRestore: "还原" | ||||||
| caption: "标题" | caption: "标题" | ||||||
| loggedInAsBot: "已登录的Bot" | loggedInAsBot: "已登录的Bot" | ||||||
|  | tools: "工具" | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。" |   description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。" | ||||||
|   sensitivity: "检测敏感度" |   sensitivity: "检测敏感度" | ||||||
|  |  | ||||||
|  | @ -717,6 +717,7 @@ accentColor: "重點色彩" | ||||||
| textColor: "文字" | textColor: "文字" | ||||||
| saveAs: "另存為..." | saveAs: "另存為..." | ||||||
| advanced: "進階" | advanced: "進階" | ||||||
|  | advancedSettings: "進階設定" | ||||||
| value: "數值" | value: "數值" | ||||||
| createdAt: "建立於" | createdAt: "建立於" | ||||||
| updatedAt: "最後更新" | updatedAt: "最後更新" | ||||||
|  | @ -913,6 +914,7 @@ windowMaximize: "最大化" | ||||||
| windowRestore: "復原" | windowRestore: "復原" | ||||||
| caption: "標題" | caption: "標題" | ||||||
| loggedInAsBot: "以機器人帳號登入中" | loggedInAsBot: "以機器人帳號登入中" | ||||||
|  | tools: "工具" | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。" |   description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。" | ||||||
|   sensitivity: "檢測敏感度" |   sensitivity: "檢測敏感度" | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
| 	"name": "misskey", | 	"name": "misskey", | ||||||
| 	"version": "13.0.0-beta.8", | 	"version": "13.0.0-beta.10", | ||||||
| 	"codename": "indigo", | 	"codename": "indigo", | ||||||
| 	"repository": { | 	"repository": { | ||||||
| 		"type": "git", | 		"type": "git", | ||||||
|  |  | ||||||
|  | @ -1,29 +1,14 @@ | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import { DataSource, In, IsNull } from 'typeorm'; | import { DataSource, In, IsNull } from 'typeorm'; | ||||||
| import { GlobalEventService } from '@/core/GlobalEventService.js'; |  | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { IdService } from '@/core/IdService.js'; | import { IdService } from '@/core/IdService.js'; | ||||||
| import type { DriveFile } from '@/models/entities/DriveFile.js'; | import type { DriveFile } from '@/models/entities/DriveFile.js'; | ||||||
| import type { Emoji } from '@/models/entities/Emoji.js'; | import type { Emoji } from '@/models/entities/Emoji.js'; | ||||||
| import { Cache } from '@/misc/cache.js'; |  | ||||||
| import type { Note } from '@/models/entities/Note.js'; |  | ||||||
| import type { EmojisRepository } from '@/models/index.js'; | import type { EmojisRepository } from '@/models/index.js'; | ||||||
| import { UtilityService } from '@/core/UtilityService.js'; |  | ||||||
| import { ReactionService } from '@/core/ReactionService.js'; |  | ||||||
| import { bindThis } from '@/decorators.js'; | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * 添付用絵文字情報 |  | ||||||
|  */ |  | ||||||
| type PopulatedEmoji = { |  | ||||||
| 	name: string; |  | ||||||
| 	url: string; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| @Injectable() | @Injectable() | ||||||
| export class CustomEmojiService { | export class CustomEmojiService { | ||||||
| 	private cache: Cache<Emoji | null>; |  | ||||||
| 
 |  | ||||||
| 	constructor( | 	constructor( | ||||||
| 		@Inject(DI.db) | 		@Inject(DI.db) | ||||||
| 		private db: DataSource, | 		private db: DataSource, | ||||||
|  | @ -32,11 +17,7 @@ export class CustomEmojiService { | ||||||
| 		private emojisRepository: EmojisRepository, | 		private emojisRepository: EmojisRepository, | ||||||
| 
 | 
 | ||||||
| 		private idService: IdService, | 		private idService: IdService, | ||||||
| 		private globalEventServie: GlobalEventService, |  | ||||||
| 		private utilityService: UtilityService, |  | ||||||
| 		private reactionService: ReactionService, |  | ||||||
| 	) { | 	) { | ||||||
| 		this.cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
|  | @ -63,117 +44,4 @@ export class CustomEmojiService { | ||||||
| 
 | 
 | ||||||
| 		return emoji; | 		return emoji; | ||||||
| 	} | 	} | ||||||
| 	 |  | ||||||
| 	@bindThis |  | ||||||
| 	private normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { |  | ||||||
| 	// クエリに使うホスト
 |  | ||||||
| 		let host = src === '.' ? null	// .はローカルホスト (ここがマッチするのはリアクションのみ)
 |  | ||||||
| 			: src === undefined ? noteUserHost	// ノートなどでホスト省略表記の場合はローカルホスト (ここがリアクションにマッチすることはない)
 |  | ||||||
| 			: this.utilityService.isSelfHost(src) ? null	// 自ホスト指定
 |  | ||||||
| 			: (src || noteUserHost);	// 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない)
 |  | ||||||
| 
 |  | ||||||
| 		host = this.utilityService.toPunyNullable(host); |  | ||||||
| 
 |  | ||||||
| 		return host; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@bindThis |  | ||||||
| 	private parseEmojiStr(emojiName: string, noteUserHost: string | null) { |  | ||||||
| 		const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); |  | ||||||
| 		if (!match) return { name: null, host: null }; |  | ||||||
| 
 |  | ||||||
| 		const name = match[1]; |  | ||||||
| 
 |  | ||||||
| 		// ホスト正規化
 |  | ||||||
| 		const host = this.utilityService.toPunyNullable(this.normalizeHost(match[2], noteUserHost)); |  | ||||||
| 
 |  | ||||||
| 		return { name, host }; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
|  * 添付用絵文字情報を解決する |  | ||||||
|  * @param emojiName ノートやユーザープロフィールに添付された、またはリアクションのカスタム絵文字名 (:は含めない, リアクションでローカルホストの場合は@.を付ける (これはdecodeReactionで可能)) |  | ||||||
|  * @param noteUserHost ノートやユーザープロフィールの所有者のホスト |  | ||||||
|  * @returns 絵文字情報, nullは未マッチを意味する |  | ||||||
|  */ |  | ||||||
| 	@bindThis |  | ||||||
| 	public async populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { |  | ||||||
| 		const { name, host } = this.parseEmojiStr(emojiName, noteUserHost); |  | ||||||
| 		if (name == null) return null; |  | ||||||
| 
 |  | ||||||
| 		const queryOrNull = async () => (await this.emojisRepository.findOneBy({ |  | ||||||
| 			name, |  | ||||||
| 			host: host ?? IsNull(), |  | ||||||
| 		})) ?? null; |  | ||||||
| 
 |  | ||||||
| 		const emoji = await this.cache.fetch(`${name} ${host}`, queryOrNull); |  | ||||||
| 
 |  | ||||||
| 		if (emoji == null) return null; |  | ||||||
| 
 |  | ||||||
| 		const isLocal = emoji.host == null; |  | ||||||
| 		// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
 |  | ||||||
| 		const emojiUrl = emoji.publicUrl || emoji.originalUrl; |  | ||||||
| 		const url = emojiUrl; |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			name: emojiName, |  | ||||||
| 			url, |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
|  * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) |  | ||||||
|  */ |  | ||||||
| 	@bindThis |  | ||||||
| 	public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<PopulatedEmoji[]> { |  | ||||||
| 		const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost))); |  | ||||||
| 		return emojis.filter((x): x is PopulatedEmoji => x != null); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@bindThis |  | ||||||
| 	public aggregateNoteEmojis(notes: Note[]) { |  | ||||||
| 		let emojis: { name: string | null; host: string | null; }[] = []; |  | ||||||
| 		for (const note of notes) { |  | ||||||
| 			emojis = emojis.concat(note.emojis |  | ||||||
| 				.map(e => this.parseEmojiStr(e, note.userHost))); |  | ||||||
| 			if (note.renote) { |  | ||||||
| 				emojis = emojis.concat(note.renote.emojis |  | ||||||
| 					.map(e => this.parseEmojiStr(e, note.renote!.userHost))); |  | ||||||
| 				if (note.renote.user) { |  | ||||||
| 					emojis = emojis.concat(note.renote.user.emojis |  | ||||||
| 						.map(e => this.parseEmojiStr(e, note.renote!.userHost))); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			const customReactions = Object.keys(note.reactions).map(x => this.reactionService.decodeReaction(x)).filter(x => x.name != null) as typeof emojis; |  | ||||||
| 			emojis = emojis.concat(customReactions); |  | ||||||
| 			if (note.user) { |  | ||||||
| 				emojis = emojis.concat(note.user.emojis |  | ||||||
| 					.map(e => this.parseEmojiStr(e, note.userHost))); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[]; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
|  * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します |  | ||||||
|  */ |  | ||||||
| 	@bindThis |  | ||||||
| 	public async prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> { |  | ||||||
| 		const notCachedEmojis = emojis.filter(emoji => this.cache.get(`${emoji.name} ${emoji.host}`) == null); |  | ||||||
| 		const emojisQuery: any[] = []; |  | ||||||
| 		const hosts = new Set(notCachedEmojis.map(e => e.host)); |  | ||||||
| 		for (const host of hosts) { |  | ||||||
| 			emojisQuery.push({ |  | ||||||
| 				name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)), |  | ||||||
| 				host: host ?? IsNull(), |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
| 		const _emojis = emojisQuery.length > 0 ? await this.emojisRepository.find({ |  | ||||||
| 			where: emojisQuery, |  | ||||||
| 			select: ['name', 'host', 'originalUrl', 'publicUrl'], |  | ||||||
| 		}) : []; |  | ||||||
| 		for (const emoji of _emojis) { |  | ||||||
| 			this.cache.set(`${emoji.name} ${emoji.host}`, emoji); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,7 +22,6 @@ export class EmojiEntityService { | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Emoji['id'] | Emoji, | 		src: Emoji['id'] | Emoji, | ||||||
| 		opts: { omitUrl?: boolean } = {}, |  | ||||||
| 	): Promise<Packed<'Emoji'>> { | 	): Promise<Packed<'Emoji'>> { | ||||||
| 		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); | 		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); | ||||||
| 
 | 
 | ||||||
|  | @ -32,17 +31,14 @@ export class EmojiEntityService { | ||||||
| 			name: emoji.name, | 			name: emoji.name, | ||||||
| 			category: emoji.category, | 			category: emoji.category, | ||||||
| 			host: emoji.host, | 			host: emoji.host, | ||||||
| 			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
 |  | ||||||
| 			url: opts.omitUrl ? undefined : (emoji.publicUrl || emoji.originalUrl), |  | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		emojis: any[], | 		emojis: any[], | ||||||
| 		opts: { omitUrl?: boolean } = {}, |  | ||||||
| 	) { | 	) { | ||||||
| 		return Promise.all(emojis.map(x => this.pack(x, opts))); | 		return Promise.all(emojis.map(x => this.pack(x))); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,12 +11,12 @@ import type { User } from '@/models/entities/User.js'; | ||||||
| import type { Note } from '@/models/entities/Note.js'; | import type { Note } from '@/models/entities/Note.js'; | ||||||
| import type { NoteReaction } from '@/models/entities/NoteReaction.js'; | import type { NoteReaction } from '@/models/entities/NoteReaction.js'; | ||||||
| import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, DriveFilesRepository } from '@/models/index.js'; | import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, DriveFilesRepository } from '@/models/index.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import type { OnModuleInit } from '@nestjs/common'; | import type { OnModuleInit } from '@nestjs/common'; | ||||||
| import type { CustomEmojiService } from '../CustomEmojiService.js'; | import type { CustomEmojiService } from '../CustomEmojiService.js'; | ||||||
| import type { ReactionService } from '../ReactionService.js'; | import type { ReactionService } from '../ReactionService.js'; | ||||||
| import type { UserEntityService } from './UserEntityService.js'; | import type { UserEntityService } from './UserEntityService.js'; | ||||||
| import type { DriveFileEntityService } from './DriveFileEntityService.js'; | import type { DriveFileEntityService } from './DriveFileEntityService.js'; | ||||||
| import { bindThis } from '@/decorators.js'; |  | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class NoteEntityService implements OnModuleInit { | export class NoteEntityService implements OnModuleInit { | ||||||
|  | @ -300,7 +300,6 @@ export class NoteEntityService implements OnModuleInit { | ||||||
| 			repliesCount: note.repliesCount, | 			repliesCount: note.repliesCount, | ||||||
| 			reactions: this.reactionService.convertLegacyReactions(note.reactions), | 			reactions: this.reactionService.convertLegacyReactions(note.reactions), | ||||||
| 			tags: note.tags.length > 0 ? note.tags : undefined, | 			tags: note.tags.length > 0 ? note.tags : undefined, | ||||||
| 			emojis: this.customEmojiService.populateEmojis(note.emojis.concat(reactionEmojiNames), host), |  | ||||||
| 			fileIds: note.fileIds, | 			fileIds: note.fileIds, | ||||||
| 			files: this.driveFileEntityService.packMany(note.fileIds), | 			files: this.driveFileEntityService.packMany(note.fileIds), | ||||||
| 			replyId: note.replyId, | 			replyId: note.replyId, | ||||||
|  | @ -385,8 +384,6 @@ export class NoteEntityService implements OnModuleInit { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		await this.customEmojiService.prefetchEmojis(this.customEmojiService.aggregateNoteEmojis(notes)); |  | ||||||
| 
 |  | ||||||
| 		return await Promise.all(notes.map(n => this.pack(n, me, { | 		return await Promise.all(notes.map(n => this.pack(n, me, { | ||||||
| 			...options, | 			...options, | ||||||
| 			_hint_: { | 			_hint_: { | ||||||
|  |  | ||||||
|  | @ -8,12 +8,12 @@ import type { Notification } from '@/models/entities/Notification.js'; | ||||||
| import type { NoteReaction } from '@/models/entities/NoteReaction.js'; | import type { NoteReaction } from '@/models/entities/NoteReaction.js'; | ||||||
| import type { Note } from '@/models/entities/Note.js'; | import type { Note } from '@/models/entities/Note.js'; | ||||||
| import type { Packed } from '@/misc/schema.js'; | import type { Packed } from '@/misc/schema.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import type { OnModuleInit } from '@nestjs/common'; | import type { OnModuleInit } from '@nestjs/common'; | ||||||
| import type { CustomEmojiService } from '../CustomEmojiService.js'; | import type { CustomEmojiService } from '../CustomEmojiService.js'; | ||||||
| import type { UserEntityService } from './UserEntityService.js'; | import type { UserEntityService } from './UserEntityService.js'; | ||||||
| import type { NoteEntityService } from './NoteEntityService.js'; | import type { NoteEntityService } from './NoteEntityService.js'; | ||||||
| import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; | import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; | ||||||
| import { bindThis } from '@/decorators.js'; |  | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class NotificationEntityService implements OnModuleInit { | export class NotificationEntityService implements OnModuleInit { | ||||||
|  | @ -143,8 +143,6 @@ export class NotificationEntityService implements OnModuleInit { | ||||||
| 			myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) ?? null); | 			myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) ?? null); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		await this.customEmojiService.prefetchEmojis(this.customEmojiService.aggregateNoteEmojis(notes)); |  | ||||||
| 
 |  | ||||||
| 		return await Promise.all(notifications.map(x => this.pack(x, { | 		return await Promise.all(notifications.map(x => this.pack(x, { | ||||||
| 			_hintForEachNotes_: { | 			_hintForEachNotes_: { | ||||||
| 				myReactions: myReactionsMap, | 				myReactions: myReactionsMap, | ||||||
|  |  | ||||||
|  | @ -392,7 +392,6 @@ export class UserEntityService implements OnModuleInit { | ||||||
| 			host: user.host, | 			host: user.host, | ||||||
| 			avatarUrl: this.getAvatarUrlSync(user), | 			avatarUrl: this.getAvatarUrlSync(user), | ||||||
| 			avatarBlurhash: user.avatar?.blurhash ?? null, | 			avatarBlurhash: user.avatar?.blurhash ?? null, | ||||||
| 			avatarColor: null, // 後方互換性のため
 |  | ||||||
| 			isAdmin: user.isAdmin ?? falsy, | 			isAdmin: user.isAdmin ?? falsy, | ||||||
| 			isModerator: user.isModerator ?? falsy, | 			isModerator: user.isModerator ?? falsy, | ||||||
| 			isBot: user.isBot ?? falsy, | 			isBot: user.isBot ?? falsy, | ||||||
|  | @ -408,9 +407,7 @@ export class UserEntityService implements OnModuleInit { | ||||||
| 				faviconUrl: instance.faviconUrl, | 				faviconUrl: instance.faviconUrl, | ||||||
| 				themeColor: instance.themeColor, | 				themeColor: instance.themeColor, | ||||||
| 			} : undefined) : undefined, | 			} : undefined) : undefined, | ||||||
| 			emojis: this.customEmojiService.populateEmojis(user.emojis, user.host), |  | ||||||
| 			onlineStatus: this.getOnlineStatus(user), | 			onlineStatus: this.getOnlineStatus(user), | ||||||
| 			driveCapacityOverrideMb: user.driveCapacityOverrideMb, |  | ||||||
| 
 | 
 | ||||||
| 			...(opts.detail ? { | 			...(opts.detail ? { | ||||||
| 				url: profile!.url, | 				url: profile!.url, | ||||||
|  | @ -420,7 +417,6 @@ export class UserEntityService implements OnModuleInit { | ||||||
| 				lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, | 				lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, | ||||||
| 				bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, | 				bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, | ||||||
| 				bannerBlurhash: user.banner?.blurhash ?? null, | 				bannerBlurhash: user.banner?.blurhash ?? null, | ||||||
| 				bannerColor: null, // 後方互換性のため
 |  | ||||||
| 				isLocked: user.isLocked, | 				isLocked: user.isLocked, | ||||||
| 				isSilenced: user.isSilenced ?? falsy, | 				isSilenced: user.isSilenced ?? falsy, | ||||||
| 				isSuspended: user.isSuspended ?? falsy, | 				isSuspended: user.isSuspended ?? falsy, | ||||||
|  | @ -447,6 +443,9 @@ export class UserEntityService implements OnModuleInit { | ||||||
| 						userId: user.id, | 						userId: user.id, | ||||||
| 					}).then(result => result >= 1) | 					}).then(result => result >= 1) | ||||||
| 					: false, | 					: false, | ||||||
|  | 				...(isMe || opts.includeSecrets ? { | ||||||
|  | 					driveCapacityOverrideMb: user.driveCapacityOverrideMb, | ||||||
|  | 				} : {}), | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 
 | 
 | ||||||
| 			...(opts.detail && isMe ? { | 			...(opts.detail && isMe ? { | ||||||
|  |  | ||||||
|  | @ -141,24 +141,6 @@ export const packedNoteSchema = { | ||||||
| 			type: 'boolean', | 			type: 'boolean', | ||||||
| 			optional: true, nullable: false, | 			optional: true, nullable: false, | ||||||
| 		}, | 		}, | ||||||
| 		emojis: { |  | ||||||
| 			type: 'array', |  | ||||||
| 			optional: false, nullable: false, |  | ||||||
| 			items: { |  | ||||||
| 				type: 'object', |  | ||||||
| 				optional: false, nullable: false, |  | ||||||
| 				properties: { |  | ||||||
| 					name: { |  | ||||||
| 						type: 'string', |  | ||||||
| 						optional: false, nullable: false, |  | ||||||
| 					}, |  | ||||||
| 					url: { |  | ||||||
| 						type: 'string', |  | ||||||
| 						optional: false, nullable: true, |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		reactions: { | 		reactions: { | ||||||
| 			type: 'object', | 			type: 'object', | ||||||
| 			optional: false, nullable: false, | 			optional: false, nullable: false, | ||||||
|  |  | ||||||
|  | @ -32,11 +32,6 @@ export const packedUserLiteSchema = { | ||||||
| 			type: 'any', | 			type: 'any', | ||||||
| 			nullable: true, optional: false, | 			nullable: true, optional: false, | ||||||
| 		}, | 		}, | ||||||
| 		avatarColor: { |  | ||||||
| 			type: 'any', |  | ||||||
| 			nullable: true, optional: false, |  | ||||||
| 			default: null, |  | ||||||
| 		}, |  | ||||||
| 		isAdmin: { | 		isAdmin: { | ||||||
| 			type: 'boolean', | 			type: 'boolean', | ||||||
| 			nullable: false, optional: true, | 			nullable: false, optional: true, | ||||||
|  | @ -55,25 +50,6 @@ export const packedUserLiteSchema = { | ||||||
| 			type: 'boolean', | 			type: 'boolean', | ||||||
| 			nullable: false, optional: true, | 			nullable: false, optional: true, | ||||||
| 		}, | 		}, | ||||||
| 		emojis: { |  | ||||||
| 			type: 'array', |  | ||||||
| 			nullable: false, optional: false, |  | ||||||
| 			items: { |  | ||||||
| 				type: 'object', |  | ||||||
| 				nullable: false, optional: false, |  | ||||||
| 				properties: { |  | ||||||
| 					name: { |  | ||||||
| 						type: 'string', |  | ||||||
| 						nullable: false, optional: false, |  | ||||||
| 					}, |  | ||||||
| 					url: { |  | ||||||
| 						type: 'string', |  | ||||||
| 						nullable: false, optional: false, |  | ||||||
| 						format: 'url', |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		onlineStatus: { | 		onlineStatus: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			format: 'url', | 			format: 'url', | ||||||
|  | @ -120,11 +96,6 @@ export const packedUserDetailedNotMeOnlySchema = { | ||||||
| 			type: 'any', | 			type: 'any', | ||||||
| 			nullable: true, optional: false, | 			nullable: true, optional: false, | ||||||
| 		}, | 		}, | ||||||
| 		bannerColor: { |  | ||||||
| 			type: 'any', |  | ||||||
| 			nullable: true, optional: false, |  | ||||||
| 			default: null, |  | ||||||
| 		}, |  | ||||||
| 		isLocked: { | 		isLocked: { | ||||||
| 			type: 'boolean', | 			type: 'boolean', | ||||||
| 			nullable: false, optional: false, | 			nullable: false, optional: false, | ||||||
|  |  | ||||||
|  | @ -309,7 +309,6 @@ export const paramDef = { | ||||||
| 	type: 'object', | 	type: 'object', | ||||||
| 	properties: { | 	properties: { | ||||||
| 		detail: { type: 'boolean', default: true }, | 		detail: { type: 'boolean', default: true }, | ||||||
| 		omitEmojiUrl: { type: 'boolean', default: false }, |  | ||||||
| 	}, | 	}, | ||||||
| 	required: [], | 	required: [], | ||||||
| } as const; | } as const; | ||||||
|  | @ -391,7 +390,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||||
| 				backgroundImageUrl: instance.backgroundImageUrl, | 				backgroundImageUrl: instance.backgroundImageUrl, | ||||||
| 				logoImageUrl: instance.logoImageUrl, | 				logoImageUrl: instance.logoImageUrl, | ||||||
| 				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
 | 				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
 | ||||||
| 				emojis: await this.emojiEntityService.packMany(emojis, { omitUrl: ps.omitEmojiUrl }), | 				emojis: await this.emojiEntityService.packMany(emojis), | ||||||
| 				defaultLightTheme: instance.defaultLightTheme, | 				defaultLightTheme: instance.defaultLightTheme, | ||||||
| 				defaultDarkTheme: instance.defaultDarkTheme, | 				defaultDarkTheme: instance.defaultDarkTheme, | ||||||
| 				ads: ads.map(ad => ({ | 				ads: ads.map(ad => ({ | ||||||
|  |  | ||||||
|  | @ -228,6 +228,8 @@ export class ClientServerService { | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			reply.header('Cache-Control', 'public, max-age=86400'); | ||||||
|  | 
 | ||||||
| 			const name = path.split('@')[0].replace('.webp', ''); | 			const name = path.split('@')[0].replace('.webp', ''); | ||||||
| 			const host = path.split('@')[1]?.replace('.webp', ''); | 			const host = path.split('@')[1]?.replace('.webp', ''); | ||||||
| 
 | 
 | ||||||
|  | @ -244,7 +246,7 @@ export class ClientServerService { | ||||||
| 
 | 
 | ||||||
| 			reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); | 			reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); | ||||||
| 
 | 
 | ||||||
| 			const url = new URL("/proxy/emoji.webp", this.config.url); | 			const url = new URL('/proxy/emoji.webp', this.config.url); | ||||||
| 			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
 | 			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
 | ||||||
| 			url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl); | 			url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl); | ||||||
| 			url.searchParams.set('emoji', '1'); | 			url.searchParams.set('emoji', '1'); | ||||||
|  | @ -347,15 +349,15 @@ export class ClientServerService { | ||||||
| 		fastify.get('/opensearch.xml', async (request, reply) => { | 		fastify.get('/opensearch.xml', async (request, reply) => { | ||||||
| 			const meta = await this.metaService.fetch(); | 			const meta = await this.metaService.fetch(); | ||||||
| 
 | 
 | ||||||
| 			const name = meta.name || "Misskey"; | 			const name = meta.name || 'Misskey'; | ||||||
| 			let content = ""; | 			let content = ''; | ||||||
| 			content += `<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">`; | 			content += '<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">'; | ||||||
| 			content += `<ShortName>${name} Search</ShortName>`; | 			content += `<ShortName>${name} Search</ShortName>`; | ||||||
| 			content += `<Description>${name} Search</Description>`; | 			content += `<Description>${name} Search</Description>`; | ||||||
| 			content += `<InputEncoding>UTF-8</InputEncoding>`; | 			content += '<InputEncoding>UTF-8</InputEncoding>'; | ||||||
| 			content += `<Image width="16" height="16" type="image/x-icon">${this.config.url}/favicon.ico</Image>`; | 			content += `<Image width="16" height="16" type="image/x-icon">${this.config.url}/favicon.ico</Image>`; | ||||||
| 			content += `<Url type="text/html" template="${this.config.url}/search?q={searchTerms}"/>`; | 			content += `<Url type="text/html" template="${this.config.url}/search?q={searchTerms}"/>`; | ||||||
| 			content += `</OpenSearchDescription>`; | 			content += '</OpenSearchDescription>'; | ||||||
| 
 | 
 | ||||||
| 			reply.header('Content-Type', 'application/opensearchdescription+xml'); | 			reply.header('Content-Type', 'application/opensearchdescription+xml'); | ||||||
| 			return await reply.send(content); | 			return await reply.send(content); | ||||||
|  |  | ||||||
|  | @ -31,7 +31,7 @@ | ||||||
| 				</MkSelect> | 				</MkSelect> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="chart _panel"> | 			<div class="chart _panel"> | ||||||
| 				<MkChart :src="chartSrc" :span="chartSpan" :limit="chartLimit" :detailed="detailed"></MkChart> | 				<MkChart :src="chartSrc" :span="chartSpan" :limit="chartLimit" :detailed="true"></MkChart> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 	</MkFolder> | 	</MkFolder> | ||||||
|  | @ -122,7 +122,7 @@ Chart.register( | ||||||
| 	Filler, | 	Filler, | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| const chartLimit = 90; | const chartLimit = 500; | ||||||
| let chartSpan = $ref<'hour' | 'day'>('hour'); | let chartSpan = $ref<'hour' | 'day'>('hour'); | ||||||
| let chartSrc = $ref('active-users'); | let chartSrc = $ref('active-users'); | ||||||
| let heatmapSrc = $ref('active-users'); | let heatmapSrc = $ref('active-users'); | ||||||
|  |  | ||||||
|  | @ -81,7 +81,6 @@ useTooltip(buttonRef, async (showing) => { | ||||||
| 	os.popup(XDetails, { | 	os.popup(XDetails, { | ||||||
| 		showing, | 		showing, | ||||||
| 		reaction: props.reaction, | 		reaction: props.reaction, | ||||||
| 		emojis: props.note.emojis, |  | ||||||
| 		users, | 		users, | ||||||
| 		count: props.count, | 		count: props.count, | ||||||
| 		targetElement: buttonRef.value, | 		targetElement: buttonRef.value, | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ async function renderChart() { | ||||||
| 	const wide = rootEl.offsetWidth > 600; | 	const wide = rootEl.offsetWidth > 600; | ||||||
| 	const narrow = rootEl.offsetWidth < 400; | 	const narrow = rootEl.offsetWidth < 400; | ||||||
| 
 | 
 | ||||||
| 	const maxDays = wide ? 20 : narrow ? 10 : 15; | 	const maxDays = wide ? 20 : narrow ? 7 : 14; | ||||||
| 
 | 
 | ||||||
| 	const raw = await os.api('retention', { }); | 	const raw = await os.api('retention', { }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,7 +15,6 @@ export const instance: Misskey.entities.InstanceMetadata = reactive(instanceData | ||||||
| export async function fetchInstance() { | export async function fetchInstance() { | ||||||
| 	const meta = await api('meta', { | 	const meta = await api('meta', { | ||||||
| 		detail: false, | 		detail: false, | ||||||
| 		omitEmojiUrl: true, |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	for (const [k, v] of Object.entries(meta)) { | 	for (const [k, v] of Object.entries(meta)) { | ||||||
|  |  | ||||||
|  | @ -76,7 +76,7 @@ | ||||||
| 		<XFederation/> | 		<XFederation/> | ||||||
| 	</MkSpacer> | 	</MkSpacer> | ||||||
| 	<MkSpacer v-else-if="tab === 'charts'" :content-max="1000" :margin-min="20"> | 	<MkSpacer v-else-if="tab === 'charts'" :content-max="1000" :margin-min="20"> | ||||||
| 		<MkInstanceStats :chart-limit="500" :detailed="true"/> | 		<MkInstanceStats/> | ||||||
| 	</MkSpacer> | 	</MkSpacer> | ||||||
| </MkStickyContainer> | </MkStickyContainer> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue