diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..c42da845b4 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f15ba3634..56c26d78e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,35 +5,39 @@ - ### Client -- Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正 -- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正 +- ### Server - --> -## 2023.x.x (unreleased) +## 2023.12.0 ### Note -- Node.js 20.10.0が最小要件になりました +- 依存関係の更新に伴い、Node.js 20.10.0が最小要件になりました +- 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします - 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。 **影響:** - それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された投稿用のピン留め絵文字が使われるため)。 + それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。 投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。 1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。 2. 「ピン留 (全般)」のタブを選択します。 - 3. 「リアクション設定からコピーする」ボタンを押すことで、アップデート前の状態に戻すことができます。 + 3. 「リアクション設定から上書きする」ボタンを押すことで、アップデート前の状態に戻すことができます。 ### General - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed) - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83) - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加 +- Enhance: 指定したドメインのメールアドレスの登録を弾くことができるように +- Enhance: 公開ロールにアサインされたときに通知が作成されるように - Enhance: アイコンデコレーションを複数設定できるように - Enhance: アイコンデコレーションの位置を微調整できるように - Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072 +- Enhance: ローカリゼーションの更新 +- Enhance: 依存関係の更新 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正 ### Client @@ -59,12 +63,17 @@ - Enhance: ユーザー名、プロフィール、お知らせ、ページの編集画面でMFMや絵文字のオートコンプリートが使用できるように - Enhance: プロフィール、お知らせの編集画面でMFMのプレビューを表示できるように - Enhance: 絵文字の詳細ページに記載される情報を追加 +- Enhance: リアクションの表示幅制限を設定可能に - Enhance: Unicode 15.0のサポート - Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように - MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました - 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります (例: ` ```js ` → Javascript, ` ```ais ` → AiScript) - Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように +- Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる +- Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加 +- Enhance: 設定したタグをトレンドに表示させないようにする項目を管理画面で設定できるように +- Enhance: 絵文字ピッカーのカテゴリに「/」を入れることでフォルダ分け表示できるように - Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367 - Fix: コードエディタが正しく表示されない問題を修正 @@ -79,10 +88,17 @@ - Fix: AiScriptの`readline`が不正な値を返すことがある問題を修正 - Fix: 投票のみ/画像のみの引用RNが、通知欄でただのRNとして判定されるバグを修正 - Fix: CWをつけて引用RNしても、普通のRNとして扱われてしまうバグを修正しました。 +- Fix: 「画像が1枚のみのメディアリストの高さ」を「デフォルト」以外に設定していると、CWの中などに添付された画像が見られないバグを修正 +- Fix: DeepL TranslationのPro accountトグルスイッチが表示されていなかったのを修正 +- Fix: twitterの埋め込みカード内リンクからリンク先を開けない問題を修正 +- Fix: WebKitブラウザー上でも「デバイスの画面を常にオンにする」機能が効くように +- Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正 +- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正 ### Server - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように - Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように +- Enhance: カスタム絵文字のインポート時の動作を改善 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303 - Fix: ロールタイムラインが保存されない問題を修正 - Fix: api.jsonの生成ロジックを改善 #12402 @@ -97,6 +113,7 @@ - Fix: 「みつける」が年越し時に壊れる問題を修正 - Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正 - Fix: モデレーションログがモデレーターは閲覧できないように修正 +- Fix: ハッシュタグのトレンド除外設定が即時に効果を持つように修正 - Fix: HTTP Digestヘッダのアルゴリズム部分に大文字の"SHA-256"しか使えない - Fix: 管理者用APIのアクセス権限が適切に設定されていない問題を修正 @@ -118,7 +135,6 @@ - 例: `$[unixtime 1701356400]` - Enhance: プラグインでエラーが発生した場合のハンドリングを強化 - Enhance: 細かなUIのブラッシュアップ -- Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加 - Fix: 効果音が再生されるとデバイスで再生している動画や音声が停止する問題を修正 #12339 - Fix: デッキに表示されたチャンネルの表示先チャンネルを切り替えた際、即座に反映されない問題を修正 #12236 - Fix: プラグインでノートの表示を書き換えられない問題を修正 @@ -146,7 +162,7 @@ ### General - Feat: アイコンデコレーション機能 - サーバーで用意された画像をアイコンに重ねることができます - - 画像のテンプレートはこちらです: https://misskey-hub.net/avatar-decoration-template.png + - 画像のテンプレートはこちらです: https://misskey-hub.net/brand-assets/ - 最大でも黄色いエリア内にデコレーションを収めることを推奨します。 - 画像は512x512pxを推奨します。 - Feat: チャンネル設定にリノート/引用リノートの可否を設定できる項目を追加 @@ -163,7 +179,7 @@ ### Client - Feat: プラグイン・テーマを外部サイトから直接インストールできるようになりました - 外部サイトでの実装が必要です。詳細は Misskey Hub をご覧ください - https://misskey-hub.net/docs/advanced/publish-on-your-website.html + https://misskey-hub.net/docs/for-developers/publish-on-your-website/ - Feat: 通知をグルーピングして表示するオプション(オプトアウト) - Feat: Misskeyの基本的なチュートリアルを実装 - Feat: スワイプしてタイムラインを再読込できるように @@ -228,7 +244,6 @@ ### Client - Enhance: TLの返信表示オプションを記憶するように - Enhance: 投稿されてから時間が経過しているノートであることを視覚的に分かりやすく -- Feat: 絵文字ピッカーのカテゴリに「/」を入れることでフォルダ分け表示できるように ### Server - Enhance: タイムライン取得時のパフォーマンスを向上 diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md index 3d0413c92f..6265124ae9 100644 --- a/CHANGELOG_CHERRYPICK.md +++ b/CHANGELOG_CHERRYPICK.md @@ -23,11 +23,6 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE # 릴리즈 노트 이 문서는 CherryPick의 변경 사항만 포함합니다. -> Misskey 또는 CherryPick v4.3.0 이전 버전에서 마이그레이션 하는 경우, 버전 관리 방식의 차이 때문에 기존 버전보다 낮은 것으로 인식되어 마이그레이션 이후 업데이트 관련 대화 상자가 표시되지 않을 수 있습니다. -> -> 또한, 일부 locale이 누락되거나 기능이 정상적으로 작동하지 않는 등의 문제가 발생할 수 있으나 이는 정상적인 동작으로, -> 문제가 발생하면 '설정 - 캐시 비우기'를 진행하거나, 브라우저 캐시를 삭제하십시오. - ## 4.x.x 출시일: unreleased
기반 Misskey 버전: 2023.x.x
@@ -35,7 +30,9 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE ### General - Change: 노트를 번역할 때 유저가 고양이로 설정되어 있으면 nyaize를 적용 +- Change: Misskey 또는 CherryPick v4.3.0 이전 버전에서 마이그레이션 시, 마이그레이션 관련 다이얼로그가 표시됨 - Feat: 리모트 서버의 이모지를 즉시 가져올 수 있음 ([pikokr/cherrypicnic@03d536c0](https://github.com/pikokr/cherrypicnic/commit/03d536c00212f2dfbebecf75e5d58e0ddb749444), [pikokr/cherrypicnic@8a2d6f3b](https://github.com/pikokr/cherrypicnic/commit/8a2d6f3b518fc13a6c32364780fba3be5eea3e5d)) + - 노트 본문 및 리액션 뷰어에서 사용 가능 - Feat: 아이콘 장식을 세부 조정할 수 있음 ([Secineralyr/misskey.dream@b3299181](https://github.com/Secineralyr/misskey.dream/commit/b329918194f1991c84633361d8a1319cf203641c), [Secineralyr/misskey.dream@1a9642bb](https://github.com/Secineralyr/misskey.dream/commit/1a9642bb9087a256522767e113c3bbfa87ec2e47)) - 위치, 크기, 불투명도를 추가로 조정할 수 있습니다. - Feat: 노트를 클릭하여 자세히 볼 수 있음 @@ -72,9 +69,18 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE - Fix: 서브 노트에서 액션 버튼의 클릭 가능 영역이 매우 작게 설정될 수 있음 - Fix: 내 프로필에서 간헐적으로 헤더에 MkFollowButton 컴포넌트가 표시될 수 있음 - Fix: 다이렉트 노트를 리노트 할 수 있음 +- Fix: 이모지를 변경할 때 이모지가 ❤️로 고정될 수 있음 +- Fix: 일부 환경에서 특정 영역에 스크롤 바가 표시될 수 있음 +- Fix: 일부 검색 페이지에서 Enter 키를 눌러 검색할 수 없음 +- Fix: 서버 이름이 매우 긴 경우, 후원 다이얼로그의 디자인이 잘못 표시될 수 있음 +- Fix: 화면이 작은 기기에서 Play의 액션 버튼이 잘려서 보일 수 있음 ### Server - Enhance: (dev) 개발 모드에서 locale 및 유형 정의가 자동으로 재생성됨 (misskey-dev/misskey#12481) +- Enhance: 푸시 알림 개선 + - 개인간 대화 알림을 받았을 때, 대화 내용을 푸시 알림에 표시 + - 그룹간 대화 알림을 받았을 때, 채팅을 보낸 사용자와 내용을 표시 + - 팔로우 알림에 Acct 및 host 정보 표시 --- diff --git a/README.md b/README.md index 980f1ad4b4..d2f7c24bfa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
- - CherryPick logo + + LycheeBridge logo **🌎 A **[CherryPick](https://github.com/kokonect-link/cherrypick)** variation in Fediverse 🚀** @@ -11,6 +11,4 @@ ## About LycheeBridge LycheeBridge는 CherryPick을 기반으로 개조된 마이크로블로깅 서비스입니다. -CherryPick은 \_KOKONECT\_에서 개발하고 있는 무료 (영원히!) 오픈 소스 분산형 소셜 미디어 플랫폼입니다! - -페이터에 가입하려면 [이 페이지](https://phater.live)를 방문해주세요. 다른 곳을 원하시나요? [\_KOKONECT\_](https://kokonect.link)는 어떠세요? +CherryPick은 \_KOKONECT\_에서 개발하고 있는 오픈 소스 분산형 소셜 미디어 플랫폼입니다. diff --git a/chart/templates/Deployment.yml b/chart/templates/Deployment.yml index f470280799..af543bd7f5 100644 --- a/chart/templates/Deployment.yml +++ b/chart/templates/Deployment.yml @@ -27,7 +27,7 @@ spec: ports: - containerPort: 3000 - name: postgres - image: postgres:14-alpine + image: postgres:15-alpine env: - name: POSTGRES_USER value: "example-cherrypick-user" @@ -38,7 +38,7 @@ spec: ports: - containerPort: 5432 - name: redis - image: redis:alpine + image: redis:7-alpine ports: - containerPort: 6379 volumes: diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index c0a3460e92..84c6e99ca8 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -120,7 +120,6 @@ sensitive: "محتوى حساس" add: "إضافة" reaction: "التفاعلات" reactions: "التفاعلات" -reactionSetting: "التفاعلات المراد عرضها في منتقي التفاعلات." reactionSettingDescription2: "اسحب لترتيب ، انقر للحذف ، استخدم \"+\" للإضافة." rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات" attachCancel: "أزل المرفق" @@ -829,8 +828,6 @@ makeReactionsPublicDescription: "هذا سيجعل قائمة تفاعلاتك classic: "تقليدي" muteThread: "اكتم النقاش" unmuteThread: "ارفع الكتم عن النقاش" -ffVisibility: "مرئية المتابِعين/المتابَعين" -ffVisibilityDescription: "يسمح لك بتحديد من يمكنهم رؤية متابِعيك ومتابَعيك." continueThread: "اعرض بقية النقاش" deleteAccountConfirm: "سيحذف حسابك نهائيًا، أتريد المتابعة؟" incorrectPassword: "كلمة السر خاطئة." @@ -961,9 +958,12 @@ rolesAssignedToMe: "الأدوار المسندة إلي" resetPasswordConfirm: "هل تريد إعادة تعيين كلمة السر؟" license: "الرخصة" unfavoriteConfirm: "أتريد إزالتها من المفضلة؟" +reactionsDisplaySize: "حجم التفاعلات" +limitWidthOfReaction: "تصغير حجم التفاعلات" noteIdOrUrl: "معرف الملاحظة أو رابطها" video: "فيديو" videos: "فيديوهات" +dataSaver: "موفر البيانات" accountMigration: "ترحيل الحساب" accountMoved: "نقل هذا المستخدم حسابه:" accountMovedShort: "رُحل هذا الحساب." @@ -971,6 +971,7 @@ operationForbidden: "عملية ممنوعة" forceShowAds: "أظهر الإعلانات التجارية دائما" reactionsList: "التفاعلات" renotesList: "إعادات النشر" +notificationDisplay: "إشعارات" leftTop: "أعلى اليسار" rightTop: "أعلى اليمين" leftBottom: "أسفل اليسار" @@ -993,6 +994,7 @@ thisChannelArchived: "أُرشفت هذه القناة." displayOfNote: "عرض الملاحظة" initialAccountSetting: "إعداد الملف الشخصي" youFollowing: "متابَع" +preventAiLearning: "منع استخدام البيانات في تعليم الآلة" options: "خيارات" specifyUser: "مستخدم محدد" failedToPreviewUrl: "تتعذر المعاينة" @@ -1006,7 +1008,16 @@ later: "لاحقاً" goToMisskey: "لCherryPick" additionalEmojiDictionary: "قواميس إيموجي إضافية" installed: "مُثبت" +enableServerMachineStats: "نشر إحصائيات عتاد الخادم" +turnOffToImprovePerformance: "تفعيله قد يزيد الأداء." +createInviteCode: "ولِّد دعوة" +inviteCodeCreated: "ولِّدت دعوة" +inviteLimitExceeded: "وصلتَ لحد عدد الدعوات المسموح لك توليدها." +createLimitRemaining: "حد عدد الدعوات: {limit} دعوة" expirationDate: "تاريخ انتهاء الصلاحية" +noExpirationDate: "لا نهاية لصلاحيتها" +inviteCodeUsedAt: "اُستخدم رمز الدعوة في" +registeredUserUsingInviteCode: "اِستخدم رمز الدعوة" unused: "غير مستعمَل" expired: "منتهية صلاحيته" icon: "الصورة الرمزية" @@ -1516,8 +1527,6 @@ _notification: youGotReply: "ردّ عليك {name}" youGotQuote: "اقتبس {name} منشورك" youRenoted: "أعاد {name} نشر منشورك" - youGotMessagingMessageFromUser: "لقد تلقيت رسالة مِن {name}" - youGotMessagingMessageFromGroup: "لقد أرسِلَت رسالة إلى الفريق {name}" youWereFollowed: "يتابعك" youReceivedFollowRequest: "تلقيتَ طلب متابعة" yourFollowRequestAccepted: "قُبل طلب المتابعة" @@ -1569,3 +1578,4 @@ _webhookSettings: _moderationLogTypes: suspend: "علِق" resetPassword: "أعد تعيين كلمتك السرية" + createInvitation: "ولِّد دعوة" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 47cb510c99..6d93ff5005 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -108,7 +108,6 @@ sensitive: "সংবেদনশীল বিষয়বস্তু" add: "যুক্ত করুন" reaction: "প্রতিক্রিয়া" reactions: "প্রতিক্রিয়া" -reactionSetting: "রিঅ্যাকশন পিকারে যেসকল প্রতিক্রিয়া দেখানো হবে" reactionSettingDescription2: "পুনরায় সাজাতে টেনে আনুন, মুছতে ক্লিক করুন, যোগ করতে + টিপুন।" rememberNoteVisibility: "নোটের দৃশ্যমান্যতার সেটিংস মনে রাখুন" attachCancel: "অ্যাটাচমেন্ট সরান " @@ -805,8 +804,6 @@ makeReactionsPublicDescription: "আপনার পূর্ববর্তী classic: "ক্লাসিক" muteThread: "থ্রেড মিউট করুন" unmuteThread: "থ্রেড আনমিউট করুন" -ffVisibility: "অনুসরণ/অনুসরণকারীদের দৃশ্যমান্যতা" -ffVisibilityDescription: "আপনি কাকে অনুসরণ করেন এবং কে আপনাকে অনুসরণ করে, সেটা কারা দেখতে পাবে তা নির্ধারণ করে।" continueThread: "আরো থ্রেড দেখুন" deleteAccountConfirm: "আপনার অ্যাকাউন্ট মুছে ফেলা হবে। ঠিক আছে?" incorrectPassword: "আপনার দেওয়া পাসওয়ার্ডটি ভুল।" @@ -1380,8 +1377,6 @@ _notification: youGotReply: "{name} আপনাকে জবাব দিয়েছে" youGotQuote: "{name} আপনাকে উদ্ধৃত করেছে" youRenoted: "{name} এর Renote" - youGotMessagingMessageFromUser: "{name} আপনাকে মেসেজ করেছে" - youGotMessagingMessageFromGroup: "{name} গ্রুপে একটি নতুন মেসেজ আছে" youWereFollowed: "আপনাকে অনুসরণ করছে" youReceivedFollowRequest: "অনুসরণ করার জন্য অনুরোধ পাওয়া গেছে" yourFollowRequestAccepted: "আপনার অনুসরণ করার অনুরোধ গৃহীত হয়েছে" diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 74ee2aa1ad..a06d1f2442 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -121,7 +121,12 @@ sensitive: "NSFW" add: "Afegir" reaction: "Reaccions" reactions: "Reaccions" -reactionSetting: "Reaccions a mostrar al selector de reaccions" +emojiPicker: "Selecció d'emojis" +pinnedEmojisForReactionSettingDescription: "Selecciona l'emoji amb el qual reaccionar" +pinnedEmojisSettingDescription: "Selecciona l'emoji amb el qual reaccionar" +emojiPickerDisplay: "Visualitza el selector d'emojis" +overwriteFromPinnedEmojisForReaction: "Reemplaça els emojis de la reacció" +overwriteFromPinnedEmojis: "Sobreescriu des dels emojis fixats" reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir." rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes" attachCancel: "Eliminar el fitxer adjunt" @@ -214,6 +219,9 @@ clearQueueConfirmText: "Les notes no lliurades que quedin a la cua no es federar clearCachedFiles: "Esborra la memòria cau" clearCachedFilesConfirm: "Segur que voleu eliminar tots els fitxers de la memòria cau?" blockedInstances: "Instàncies bloquejades" +blockedInstancesDescription: "Llista els enllaços d'amfitrió de les instàncies que vols bloquejar separades per un salt de pàgina. Les instàncies llistades no podran comunicar-se amb aquesta instància." +silencedInstances: "Instàncies silenciades" +silencedInstancesDescription: "Llista els enllaços d'amfitrió de les instàncies que vols silenciar. Tots els comptes de les instàncies llistades s'establiran com silenciades i només podran fer sol·licitacions de seguiment, i no podran mencionar als comptes locals si no els segueixen. Això no afectarà les instàncies bloquejades." muteAndBlock: "Silencia i bloca" mutedUsers: "Usuaris silenciats" blockedUsers: "Usuaris bloquejats" @@ -228,9 +236,12 @@ preview: "Vista prèvia" default: "Per defecte" defaultValueIs: "Per defecte: {value}" noCustomEmojis: "Cap emoji personalitzat" +noJobs: "No hi ha feines" federating: "Federant" blocked: "Bloquejat" suspended: "Suspés" +all: "tot" +subscribing: "Subscrit a" publishing: "S'està publicant" notResponding: "Sense resposta" instanceFollowing: "Seguits del servidor" @@ -255,11 +266,31 @@ removed: "Eliminat" removeAreYouSure: "Segur que voleu retirar «{x}»?" deleteAreYouSure: "Segur que voleu retirar «{x}»?" resetAreYouSure: "Segur que voleu restablir-ho?" +areYouSure: "Està segur?" saved: "S'ha desat" messaging: "Xat" upload: "Puja" +keepOriginalUploading: "Guarda la imatge original" +keepOriginalUploadingDescription: "Guarda la imatge pujada com hi és. Si està apagat, una versió per a la visualització a la xarxa serà generada quan sigui pujada." +fromDrive: "Des de la unitat" +fromUrl: "Des d'un enllaç" +uploadFromUrl: "Carrega des d'un enllaç" +uploadFromUrlDescription: "Enllaç del fitxer que vols carregar" +uploadFromUrlRequested: "Càrrega sol·licitada" +uploadFromUrlMayTakeTime: "La càrrega des de l'enllaç pot prendre un temps" +explore: "Explora" +messageRead: "Vist" +noMoreHistory: "No hi resta més per veure" +startMessaging: "Començar a xatejar" +nUsersRead: "Vist per {n}" +agreeTo: "Accepto que {0}" +agree: "Hi estic d'acord" +agreeBelow: "Hi estic d'acord amb el següent" +basicNotesBeforeCreateAccount: "Notes importants" +termsOfService: "Condicions d'ús" start: "Comença" home: "Inici" +remoteUserCaution: "Ja que aquest usuari resideix a una instància remota, la informació mostrada es podria trobar incompleta." activity: "Activitat" images: "Imatges" image: "Imatges" @@ -275,16 +306,34 @@ dark: "Fosc" lightThemes: "Temes clars" darkThemes: "Temes foscos" syncDeviceDarkMode: "Sincronitza el mode fosc amb la configuració del dispositiu" +drive: "Unitat" +fileName: "Nom del Fitxer" +selectFile: "Selecciona fitxers" +selectFiles: "Selecciona fitxers" +selectFolder: "Selecció de carpeta" +selectFolders: "Selecció de carpeta" renameFile: "Canvia el nom del fitxer" folderName: "Nom de la carpeta" createFolder: "Crea una carpeta" renameFolder: "Canvia el nom de la carpeta" deleteFolder: "Elimina la carpeta" +folder: "Carpeta " addFile: "Afegeix un fitxer" +emptyDrive: "La teva unitat és buida" emptyFolder: "La carpeta està buida" unableToDelete: "No es pot eliminar" +inputNewFileName: "Introduïu el nom de fitxer nou" +inputNewDescription: "Inserta una nova llegenda" +inputNewFolderName: "Introduïu el nom de la carpeta nova" +circularReferenceFolder: "La carpeta destinatària és una subcarpeta de la carpeta a la qual la desitges moure" +hasChildFilesOrFolders: "No és possible esborrar aquesta carpeta ja que no és buida" copyUrl: "Copia l'URL" rename: "Canvia el nom" +avatar: "Icona" +banner: "Bàner" +displayOfSensitiveMedia: "Visualització de contingut sensible" +whenServerDisconnected: "Quan es perdi la connexió al servidor" +disconnectedFromServer: "Desconnectat pel servidor" reload: "Actualitza" doNothing: "Ignora" accept: "Accepta" @@ -354,33 +403,132 @@ notFound: "No s'ha trobat" markAsReadAllUnreadNotes: "Marca-ho tot com a llegit" help: "Ajuda" invites: "Convida" +title: "Títol" +text: "Text" +enable: "Habilita" next: "Següent" +retype: "Torneu a introduir-la" noteOf: "Publicació de: {user}" +quoteAttached: "Frase adjunta" +quoteQuestion: "Vols annexar-la com a cita?" +noMessagesYet: "Encara no hi ha missatges" +newMessageExists: "Has rebut un nou missatge" +onlyOneFileCanBeAttached: "Només pots adjuntar un fitxer a un missatge" +signinRequired: "Si us plau, Registra't o inicia la sessió abans de continuar" invitations: "Convida" +invitationCode: "Codi d'invitació" +checking: "Comprovació en curs..." +available: "Disponible" +unavailable: "No és disponible" +usernameInvalidFormat: "Pots fer servir lletres (majúscules i minúscules), números i barres baixes (\"_\")" +tooShort: "Massa curt" +tooLong: "Massa llarg" +weakPassword: "Contrasenya insegura" +normalPassword: "Bona contrasenya" +strongPassword: "Contrasenya segura" +passwordMatched: "Correcte!" +passwordNotMatched: "No coincideix" +signinWith: "Inicia sessió amb amb {x}" +signinFailed: "Autenticació sense èxit. Intenta-ho un altre cop utilitzant la contrasenya i el nom correctes." +or: "O" +language: "Idioma" +uiLanguage: "Idioma de l'interfície" +aboutX: "Respecte a {x}" +emojiStyle: "Estil d'emoji" +native: "Nadiu" +disableDrawer: "No mostrar els menús en calaixos" +showNoteActionsOnlyHover: "Només mostra accions de la nota en passar amb el cursor" +noHistory: "No hi ha un registre previ" +signinHistory: "Historial d'autenticacions" +enableAdvancedMfm: "Habilitar l'MFM avançat" +enableAnimatedMfm: "Habilitar l'MFM amb moviment" +doing: "Processant..." +category: "Categoria" tags: "Etiquetes" docSource: "Font del document" createAccount: "Crea un compte" existingAccount: "Compte existent" regenerate: "Regenera" fontSize: "Mida del text" +mediaListWithOneImageAppearance: "Altura de la llista de fitxers amb una única imatge" +limitTo: "Limita a {x}" noFollowRequests: "No tens sol·licituds de seguiment" +openImageInNewTab: "Obre imatges a una nova pestanya" dashboard: "Panell de control" local: "Local" remote: "Remot" total: "Total" +weekOverWeekChanges: "Canvis l'última setmana" +dayOverDayChanges: "Canvis ahir" appearance: "Aparença" clientSettings: "Configuració del client" accountSettings: "Configuració del compte" +promotion: "Promocionat" +promote: "Promoure" +numberOfDays: "Nombre de dies" hideThisNote: "Amaga la publicació" showFeaturedNotesInTimeline: "Mostra publicacions destacades en la línia de temps" +objectStorage: "Emmagatzematge d'objectes\n" +useObjectStorage: "Utilitzar l'emmagatzematge d'objectes" +objectStorageBaseUrl: "Base d'enllaç" +objectStorageBaseUrlDesc: "Prefix d'enllaç utilitzat per a fer referencia als fitxers. Especifica l'enllaç del teu CDN o Proxy si n'estàs utilitzant qualsevol, en cas contrari, especifica l'enllaç al que es pot accedir públicament segons la guia de servei que vosté utilitza.\nPer l'ús d'S3 utilitza 'https://.s3.amazonaws.com' I per a GCS o serveis equivalents utilitza 'https://storage.googleapis.com/'." newNoteRecived: "Hi ha publicacions noves" installedDate: "Data d'instal·lació" state: "Estat" sort: "Ordena" ascendingOrder: "Ascendent" descendingOrder: "Descendent" +removeAllFollowing: "Deixar de seguir tots els usuaris seguits" +removeAllFollowingDescription: "El fet d'executar això, et farà deixar de seguir a tots els usuaris de {host}. Si us plau, executa això si l'amfitrió, per exemple, ja no existeix." +userSuspended: "Aquest usuari ha sigut suspès" +userSilenced: "Aquest usuari està sent silenciat" +yourAccountSuspendedTitle: "Aquest compte és suspès" +yourAccountSuspendedDescription: "Aquest compte ha sigut suspès a causa de la violació de les condicions d'ús o similars. Contacta l'administrador si en vol saber més. Si us plau, no en faci un altre compte." +tokenRevoked: "Codi de seguretat no vàlid" +tokenRevokedDescription: "La petició més recent ha estat denegada perquè contenia un codi de seguretat no vàlid. Actualitza la pàgina i torna-ho a provar." +accountDeleted: "Compte eliminat amb èxit" +accountDeletedDescription: "Aquest compte ha sigut eliminat" +menu: "Menú" +divider: "Divisor" +addItem: "Afegir element" +rearrange: "Torna a ordenar" +relays: "Relés" +addRelay: "Afegeix relés" +inboxUrl: "Enllaç de la safata d'entrada" +addedRelays: "Relés afegits" +serviceworkerInfo: "És obligatòria l'activació per a obtenir notificacions push" deletedNote: "Publicacions eliminades" invisibleNote: "Publicacions amagades" +enableInfiniteScroll: "Carrega més automàticament\n" +visibility: "Visibilitat" +poll: "Enquesta" +useCw: "Amaga el contingut" +enablePlayer: "Obre el reproductor de vídeo" +disablePlayer: "Tanca el reproductor de vídeo" +expandTweet: "Expandir post" +themeEditor: "Editor de temes" +description: "Descripció" +describeFile: "Afegir subtitulació" +enterFileDescription: "Afegeix un títol" +author: "Autor" +leaveConfirm: "Hi ha canvis sense guardar. Els vols descartar?" +manage: "Administració" +plugins: "Extensions" +preferencesBackups: "Configuracions de les Còpies de seguretat" +deck: "Escriptori" +undeck: "Tanca l'escriptori" +useBlurEffectForModal: "Utilitzar l'efecte de difuminació a modals" +useFullReactionPicker: "Utilitza el cercador de reaccions d'escala sencera" +width: "Amplada" +height: "Alçària" +large: "Gran" +medium: "Mitjà" +small: "Petit" +generateAccessToken: "Genera codi d'accés" +permission: "Permisos" +enableAll: "Habilita tot" +disableAll: "Deshabilita tot" +tokenRequested: "Donar accés al compte" smtpHost: "Amfitrió" smtpUser: "Nom d'usuari" smtpPass: "Contrasenya" @@ -390,12 +538,17 @@ clearCache: "Esborra la memòria cau" showingPastTimeline: "Estàs veient una línia de temps antiga" info: "Informació" user: "Usuaris" +administration: "Administració" +middle: "Mitjà" global: "Global" searchByGoogle: "Cercar" file: "Fitxers" +icon: "Icona" replies: "Respondre" renotes: "Impulsa" _role: + _priority: + middle: "Mitjà" _options: antennaMax: "Nombre màxim d'antenes" _email: @@ -404,9 +557,11 @@ _email: _instanceMute: instanceMuteDescription: "Silencia tots els impulsos dels servidors seleccionats, també els usuaris que responen a altres d'un servidor silenciat." _theme: + description: "Descripció" keys: mention: "Menció" renote: "Renotar" + divider: "Divisor" _sfx: note: "Notes" notification: "Notificacions" @@ -449,6 +604,8 @@ _timelines: local: "Local" social: "Social" global: "Global" +_play: + summary: "Descripció" _pages: contents: "Contingut" blocks: diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 881de5cb2a..55f4d19432 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -120,7 +120,6 @@ sensitive: "NSFW" add: "Přidat" reaction: "Reakce" reactions: "Reakce" -reactionSetting: "Reakce zobrazené ve výběru reakcí" reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte \"+\" k přidání" rememberNoteVisibility: "Zapamatovat nastavení zobrazení poznámky" attachCancel: "Odstranit přílohu" @@ -855,8 +854,6 @@ makeReactionsPublicDescription: "Tohle zviditelný seznam vašich předchozích classic: "Klasický" muteThread: "Ztlumit vlákno" unmuteThread: "Zrušit ztlumení vlákna" -ffVisibility: "Viditelnost Sledovaných/Sledujících" -ffVisibilityDescription: "Umožní vám nastavit kdo uvidí koho sledujete a kdo vás sleduje." continueThread: "Zobrazit pokračování vlákna" deleteAccountConfirm: "Tohle nenávratně smaže váš účet, chcete pokračovat?" incorrectPassword: "Nesprávné heslo." diff --git a/locales/de-DE.yml b/locales/de-DE.yml index cae0a1cb42..a50e399ffc 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -121,7 +121,6 @@ sensitive: "Sensibel" add: "Hinzufügen" reaction: "Reaktionen" reactions: "Reaktionen" -reactionSetting: "In der Reaktionsauswahl anzuzeigende Reaktionen" reactionSettingDescription2: "Ziehe um Anzuordnen, klicke um zu löschen, drücke „+“ um hinzuzufügen" rememberNoteVisibility: "Notizsichtbarkeit merken" attachCancel: "Anhang entfernen" @@ -886,8 +885,6 @@ makeReactionsPublicDescription: "Jeder wird die Liste deiner gesendeten Reaktion classic: "Classic" muteThread: "Thread stummschalten" unmuteThread: "Threadstummschaltung aufheben" -ffVisibility: "Sichtbarkeit von Gefolgten/Followern" -ffVisibilityDescription: "Konfiguriere wer sehen kann, wem du folgst sowie wer dir folgt." continueThread: "Weiteren Threadverlauf anzeigen" deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?" incorrectPassword: "Falsches Passwort." @@ -2155,8 +2152,6 @@ _notification: youGotReply: "{name} hat dir geantwortet" youGotQuote: "{name} hat dich zitiert" youRenoted: "Renote deiner Notiz von {name}" - youGotMessagingMessageFromUser: "{name} hat dir eine Chatnachricht gesendet" - youGotMessagingMessageFromGroup: "In die Gruppe {name} wurde eine Chatnachricht gesendet" youWereFollowed: "ist dir gefolgt" youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten" yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert" diff --git a/locales/el-GR.yml b/locales/el-GR.yml index e862502f77..6a285118de 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -104,7 +104,6 @@ clickToShow: "Κάντε κλικ για εμφάνιση" add: "Προσθέστε" reaction: "Αντιδράσεις" reactions: "Αντιδράσεις" -reactionSetting: "Αντιδράσεις για εμφάνιση στην επιλογή αντίδρασης" reactionSettingDescription2: "Σύρετε για να αλλάξετε τη σειρά, κάντε κλικ για να διαγράψετε, πατήστε \"+\" για να προσθέσετε." rememberNoteVisibility: "Θυμήσου τις ρυθμίσεις ορατότητας σημειώματος" attachCancel: "Διαγραφή αρχείου" diff --git a/locales/en-US.yml b/locales/en-US.yml index d2f873b8fd..e2edfcf11f 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1,5 +1,9 @@ --- _lang_: "English" +welcome: "Welcome!" +cherrypickMigrated: "The migration to CherryPick is complete!" +cherrypickMigratedCacheClearTitle: "The cache must be cleared." +cherrypickMigratedCacheClear: "This server was migrated from Misskey or CherryPick v4.3.0 or earlier.\nBecause of the different versioning, any leftover cache can cause problems, so you'll need to clear the cache on the first connection after the migration.\n\nThis will only happen the first time." showRenoteVisibilitySelector: "Show renote visibility selector" cannotBeUsedFunc: "This feature is currently unavailable." scale: "Scale" @@ -191,7 +195,6 @@ sensitive: "Sensitive" add: "Add" reaction: "Reactions" reactions: "Reactions" -reactionSetting: "Reactions to show in the reaction picker" reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add." rememberNoteVisibility: "Remember note visibility settings" attachCancel: "Remove attachment" @@ -634,7 +637,7 @@ showInPage: "Show in page" popout: "Pop-out" volume: "Volume" masterVolume: "Master volume" -notUseSound: "No sounds output." +notUseSound: "Disable sound" useSoundOnlyWhenActive: "Output sounds only if CherryPick is active." details: "Details" chooseEmoji: "Select an emoji" @@ -968,8 +971,6 @@ makeReactionsPublicDescription: "This will make the list of all your past reacti classic: "Classic" muteThread: "Mute thread" unmuteThread: "Unmute thread" -ffVisibility: "Follows/Followers Visibility" -ffVisibilityDescription: "Allows you to configure who can see who you follow and who follows you." continueThread: "View thread continuation" deleteAccountConfirm: "This will irreversibly delete your account. Proceed?" incorrectPassword: "Incorrect password." @@ -1279,6 +1280,7 @@ cwNotationRequired: "If \"Hide content\" is enabled, a description must be provi doReaction: "Add reaction" code: "Code" reloadRequiredToApplySettings: "Reloading is required to apply the settings." +decorate: "Decorate" showUnreadNotificationsCount: "Show the number of unread notifications" showCatOnly: "Show only cats" additionalPermissionsForFlash: "Allow to add permission to Play" @@ -1441,7 +1443,7 @@ _initialTutorial: sensitiveSucceeded: "When attaching files, please set sensitivities in accordance with the server guidelines." doItToContinue: "Mark the attachment file as sensitive to proceed." _done: - title: "The tutorial is complete! 🎉" + title: "You've completed the tutorial! 🎉" description: "The functions introduced here are just a small part. For a more detailed understanding of using LycheeBridge, please refer to {link}." _timelineDescription: home: "In the Home timeline, you can see notes from accounts you follow." @@ -2456,8 +2458,6 @@ _notification: youGotReply: "{name} replied to you" youGotQuote: "{name} quoted you" youRenoted: "Renote from {name}" - youGotMessagingMessageFromUser: "{name} sent you a chat message" - youGotMessagingMessageFromGroup: "A chat message was sent to the {name} group" youWereFollowed: "followed you" youReceivedFollowRequest: "You've received a follow request" yourFollowRequestAccepted: "Your follow request was accepted" @@ -2465,6 +2465,7 @@ _notification: pollEnded: "Poll results have become available" newNote: "New note" unreadAntennaNote: "Antenna {name}" + roleAssigned: "Role given" emptyPushNotificationMessage: "Push notifications have been updated" achievementEarned: "Achievement unlocked" testNotification: "Test notification" @@ -2487,6 +2488,7 @@ _notification: receiveFollowRequest: "Received follow requests" followRequestAccepted: "Accepted follow requests" groupInvited: "Group invitations" + roleAssigned: "Role given" achievementEarned: "Achievement unlocked" app: "Notifications from linked apps" _actions: diff --git a/locales/es-ES.yml b/locales/es-ES.yml index d19885b170..1b4c38e31b 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -121,7 +121,6 @@ sensitive: "Marcado como sensible" add: "Agregar" reaction: "Reacción" reactions: "Reacción" -reactionSetting: "Reacciones para mostrar en el menú de reacciones" reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir." rememberNoteVisibility: "Recordar visibilidad" attachCancel: "Quitar adjunto" @@ -886,8 +885,6 @@ makeReactionsPublicDescription: "Todas las reacciones que hayas hecho serán pú classic: "Clásico" muteThread: "Silenciar hilo" unmuteThread: "Mostrar hilo" -ffVisibility: "Visibilidad de seguidores y seguidos" -ffVisibilityDescription: "Puedes configurar quien puede ver a quienes sigues y quienes te siguen" continueThread: "Ver la continuación del hilo" deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?" incorrectPassword: "La contraseña es incorrecta" @@ -2173,8 +2170,6 @@ _notification: youGotReply: "Respuesta de {name}" youGotQuote: "Citado por {name}" youRenoted: "Renotado por {name}" - youGotMessagingMessageFromUser: "{name} comenzó un chat contigo" - youGotMessagingMessageFromGroup: "Tienes un chat de {name}" youWereFollowed: "te ha seguido" youReceivedFollowRequest: "Has mandado una solicitud de seguimiento" yourFollowRequestAccepted: "Tu solicitud de seguimiento fue aceptada" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 562798705e..738af01fbc 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -121,7 +121,12 @@ sensitive: "Contenu sensible" add: "Ajouter" reaction: "Réactions" reactions: "Réactions" -reactionSetting: "Réactions à afficher dans le sélecteur de réactions" +emojiPicker: "Sélecteur d’émojis" +pinnedEmojisForReactionSettingDescription: "Vous pouvez définir les émojis épinglés lors de la réaction" +pinnedEmojisSettingDescription: "Vous pouvez définir les émojis épinglés lors de la saisie de l'émoji" +emojiPickerDisplay: "Affichage du sélecteur d'émojis" +overwriteFromPinnedEmojisForReaction: "Remplacer par les émojis épinglés pour la réaction" +overwriteFromPinnedEmojis: "Remplacer par les émojis épinglés globalement" reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter." rememberNoteVisibility: "Se souvenir de la visibilité des notes" attachCancel: "Supprimer le fichier attaché" @@ -885,8 +890,8 @@ makeReactionsPublicDescription: "Ceci rendra la liste de toutes vos réactions d classic: "Classique" muteThread: "Masquer cette discussion" unmuteThread: "Ne plus masquer le fil" -ffVisibility: "Visibilité des abonnés/abonnements" -ffVisibilityDescription: "Permet de configurer qui peut voir les personnes que tu suis et les personnes qui te suivent." +followingVisibility: "Visibilité des abonnements" +followersVisibility: "Visibilité des abonnés" continueThread: "Afficher la suite du fil" deleteAccountConfirm: "Votre compte sera supprimé. Êtes vous certain ?" incorrectPassword: "Le mot de passe est incorrect." @@ -1038,6 +1043,8 @@ license: "Licence" myClips: "Mes clips" drivecleaner: "Nettoyeur du Disque" retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur." +enableChartsForRemoteUser: "Générer les graphiques pour les utilisateurs distants" +enableChartsForFederatedInstances: "Générer les graphiques pour les instances distantes" showClipButtonInNoteFooter: "Ajouter « Clip » au menu d'action de la note" reactionsDisplaySize: "Taille de l'affichage des réactions" limitWidthOfReaction: "Limiter la largeur maximale des réactions et les afficher en taille réduite" @@ -1081,6 +1088,7 @@ options: "Options" specifyUser: "Spécifier l'utilisateur·rice" failedToPreviewUrl: "Aperçu d'URL échoué" update: "Mettre à jour" +rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction" later: "Plus tard" goToMisskey: "Retour vers CherryPick" additionalEmojiDictionary: "Dictionnaires d'émojis additionnels" @@ -1143,6 +1151,9 @@ doReaction: "Réagir" code: "Code" reloadRequiredToApplySettings: "Le rafraîchissement est nécessaire pour que les paramètres prennent effet." remainingN: "Restants : {n}" +overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?" +seasonalScreenEffect: "Effet d'écran saisonnier" +decorate: "Décorer" _announcement: readConfirmTitle: "Marquer comme lu ?" shouldNotBeUsedToPresentPermanentInfo: "Puisque cela pourrait nuire considérablement à l'expérience utilisateur pour les nouveaux utilisateurs, il est recommandé d'utiliser les annonces pour afficher des informations temporaires plutôt que des informations persistantes." @@ -1969,14 +1980,13 @@ _notification: youGotReply: "Réponse de {name}" youGotQuote: "Cité·e par {name}" youRenoted: "{name} vous a Renoté" - youGotMessagingMessageFromUser: "{name} vous envoyé un message" - youGotMessagingMessageFromGroup: "Un message a été envoyé au groupe {name}" youWereFollowed: "s'est abonné·e à vous" youReceivedFollowRequest: "Vous avez reçu une demande d’abonnement" yourFollowRequestAccepted: "Votre demande d’abonnement a été accepté" youWereInvitedToGroup: "Invité·e au groupe" pollEnded: "Les résultats du sondage sont disponibles" unreadAntennaNote: "Antenne {name}" + roleAssigned: "Rôle attribué" emptyPushNotificationMessage: "Les notifications push ont été mises à jour" achievementEarned: "Accomplissement" testNotification: "Tester la notification" @@ -1995,6 +2005,7 @@ _notification: receiveFollowRequest: "Demande d'abonnement reçue" followRequestAccepted: "Demande d'abonnement acceptée" groupInvited: "Invitation à un groupe" + roleAssigned: "Rôle reçu" achievementEarned: "Accomplissement" app: "Notifications provenant des apps" _actions: diff --git a/locales/id-ID.yml b/locales/id-ID.yml index f5583dddd8..0fc322c9e2 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -121,7 +121,6 @@ sensitive: "Konten sensitif" add: "Tambahkan" reaction: "Reaksi" reactions: "Reaksi" -reactionSetting: "Reaksi untuk dimunculkan di bilah reaksi" reactionSettingDescription2: "Geser untuk memindah urutan emoji, klik untuk menghapus, tekan \"+\" untuk menambahkan" rememberNoteVisibility: "Ingat pengaturan visibilitas catatan" attachCancel: "Hapus lampiran" @@ -261,6 +260,7 @@ removed: "Telah dihapus" removeAreYouSure: "Apakah kamu yakin ingin menghapus \"{x}\"?" deleteAreYouSure: "Apakah kamu yakin ingin menghapus \"{x}\"?" resetAreYouSure: "Yakin mau atur ulang?" +areYouSure: "Apakah kamu yakin?" saved: "Telah disimpan" messaging: "Pesan" upload: "Unggah" @@ -311,6 +311,7 @@ folderName: "Nama folder" createFolder: "Buat folder" renameFolder: "Ubah nama folder" deleteFolder: "Hapus folder" +folder: "Folder" addFile: "Tambahkan berkas" emptyDrive: "Drive kosong" emptyFolder: "Folder kosong" @@ -555,6 +556,8 @@ showInPage: "Tampilkan di halaman" popout: "Pop-out" volume: "Volume" masterVolume: "Master volume" +notUseSound: "Tidak ada keluaran suara" +useSoundOnlyWhenActive: "Hanya keluarkan suara jika Misskey sedang aktif" details: "Selengkapnya" chooseEmoji: "Pilih emoji" unableToProcess: "Operasi tersebut tidak dapat diselesaikan." @@ -883,8 +886,6 @@ makeReactionsPublicDescription: "Pengaturan ini akan membuat daftar dari semua r classic: "Klasik" muteThread: "Bisukan thread" unmuteThread: "Suarakan thread" -ffVisibility: "Visibilitas Mengikuti/Pengikut" -ffVisibilityDescription: "Mengatur siapa yang dapat melihat pengikutmu dan yang kamu ikuti." continueThread: "Lihat lanjutan thread" deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?" incorrectPassword: "Kata sandi salah." @@ -1037,6 +1038,8 @@ resetPasswordConfirm: "Yakin untuk mereset kata sandimu?" 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." sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler." +hiddenTags: "Tagar tersembunyi" +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." license: "Lisensi" unfavoriteConfirm: "Yakin ingin menghapusnya dari favorit?" @@ -1049,6 +1052,7 @@ enableChartsForRemoteUser: "Buat bagan data pengguna instansi luar" enableChartsForFederatedInstances: "Buat bagan data peladen instansi luar" showClipButtonInNoteFooter: "Tambahkan \"Klip\" ke menu aksi catatan" reactionsDisplaySize: "Ukuran tampilan reaksi" +limitWidthOfReaction: "Batasi lebar maksimum reaksi dan tampilkan dalam ukuran terbatasi." noteIdOrUrl: "ID catatan atau URL" video: "Video" videos: "Video" @@ -1175,6 +1179,9 @@ useGroupedNotifications: "Tampilkan notifikasi secara dikelompokkan" signupPendingError: "Terdapat masalah ketika memverifikasi alamat surel. Tautan kemungkinan telah kedaluwarsa." cwNotationRequired: "Jika \"Sembunyikan konten\" diaktifkan, deskripsi harus disediakan." doReaction: "Tambahkan reaksi" +code: "Kode" +reloadRequiredToApplySettings: "Muat ulang diperlukan untuk menerapkan pengaturan." +remainingN: "Sisa : {n}" _announcement: forExistingUsers: "Hanya pengguna yang telah ada" forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya." @@ -1203,12 +1210,17 @@ _initialAccountSetting: _initialTutorial: launchTutorial: "Lihat Tutorial" title: "Tutorial" + wellDone: "Kerja bagus!" skipAreYouSure: "Berhenti dari Tutorial?" _landing: title: "Selamat datang di Tutorial" description: "Di sini kamu dapat mempelajari dasar-dasar dari penggunaan CherryPick dan fitur-fiturnya." _note: title: "Apa itu Catatan?" + _reaction: + title: "Apa itu Reaksi?" + _timeline: + title: "Konsep Lini Masa" _postNote: title: "Pengaturan posting Catatan" _visibility: @@ -1216,6 +1228,12 @@ _initialTutorial: home: "Hanya publik ke lini masa Beranda. Pengguna yang mengunjungi profilmu melalui pengikut dan renote dapat melihatnya." followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun." direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung." + _cw: + _exampleNote: + cw: "Peringatan: Bikin Lapar!" + note: "Baru aja makan donat berlapis coklat 🍩😋" + _howToMakeAttachmentsSensitive: + title: "Bagaimana menandai lampiran sebagai sensitif?" _serverRules: description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan." _serverSettings: @@ -2146,8 +2164,6 @@ _notification: youGotReply: "{name} membalas kamu" youGotQuote: "{name} mengutip kamu" youRenoted: "{name} me-renote kamu" - youGotMessagingMessageFromUser: "{name} mengirimi kamu pesan" - youGotMessagingMessageFromGroup: "Sebuah pesan telah dikirim ke grup {name}" youWereFollowed: "Mengikuti kamu" youReceivedFollowRequest: "Kamu menerima permintaan mengikuti" yourFollowRequestAccepted: "Permintaan mengikuti kamu telah diterima" diff --git a/locales/index.d.ts b/locales/index.d.ts index 5c9db9ffb6..5fcb0555b2 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -3,6 +3,10 @@ // Do not edit this file directly. export interface Locale { "_lang_": string; + "welcome": string; + "cherrypickMigrated": string; + "cherrypickMigratedCacheClearTitle": string; + "cherrypickMigratedCacheClear": string; "showRenoteVisibilitySelector": string; "cannotBeUsedFunc": string; "scale": string; @@ -1973,6 +1977,7 @@ export interface Locale { "disposable": string; "mx": string; "smtp": string; + "banned": string; }; "_ffVisibility": { "public": string; @@ -2639,8 +2644,6 @@ export interface Locale { "youGotReply": string; "youGotQuote": string; "youRenoted": string; - "youGotMessagingMessageFromUser": string; - "youGotMessagingMessageFromGroup": string; "youWereFollowed": string; "youReceivedFollowRequest": string; "yourFollowRequestAccepted": string; @@ -2648,6 +2651,7 @@ export interface Locale { "pollEnded": string; "newNote": string; "unreadAntennaNote": string; + "roleAssigned": string; "emptyPushNotificationMessage": string; "achievementEarned": string; "testNotification": string; @@ -2670,6 +2674,7 @@ export interface Locale { "receiveFollowRequest": string; "followRequestAccepted": string; "groupInvited": string; + "roleAssigned": string; "achievementEarned": string; "app": string; }; diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 5d71b7f1b5..8e7afc891c 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -121,7 +121,12 @@ sensitive: "Allegato esplicito" add: "Aggiungi" reaction: "Reazioni" reactions: "Reazioni" -reactionSetting: "Reazioni visualizzate sul pannello" +emojiPicker: "Selettore emoji" +pinnedEmojisForReactionSettingDescription: "Scegli quale sia l'emoji in cima, quando reagisci" +pinnedEmojisSettingDescription: "Scegli quale sia l'emoji in cima, quando reagisci" +emojiPickerDisplay: "Visualizza selettore" +overwriteFromPinnedEmojisForReaction: "Sovrascrivi con le impostazioni reazioni" +overwriteFromPinnedEmojis: "Sovrascrivi con le impostazioni globali" reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" attachCancel: "Rimuovi allegato" @@ -261,6 +266,7 @@ removed: "Eliminato con successo" removeAreYouSure: "Vuoi davvero eliminare \"{x}\"?" deleteAreYouSure: "Vuoi davvero eliminare \"{x}\"?" resetAreYouSure: "Ripristinare?" +areYouSure: "Confermi?" saved: "Salvato" messaging: "Messaggi" upload: "Carica" @@ -887,8 +893,6 @@ makeReactionsPublicDescription: "La lista delle reazioni che avete fatto è a di classic: "Classico" muteThread: "Silenzia conversazione" unmuteThread: "Riattiva la conversazione" -ffVisibility: "Visibilità delle connessioni" -ffVisibilityDescription: "Puoi scegliere a chi mostrare le tue relazioni con altri profili nel fediverso." continueThread: "Altre conversazioni" deleteAccountConfirm: "Così verrà eliminato il profilo. Vuoi procedere?" incorrectPassword: "La password è errata." @@ -1171,6 +1175,7 @@ tosAndPrivacyPolicy: "Condizioni d'uso e informativa privacy" avatarDecorations: "Decorazioni foto profilo" attach: "Applica" detach: "Rimuovi" +detachAll: "Togli tutto" angle: "Angolo" flip: "Inverti" showAvatarDecorations: "Mostra decorazione della foto profilo" @@ -1184,6 +1189,10 @@ cwNotationRequired: "Devi indicare perché il contenuto è indicato come esplici doReaction: "Reagisci" code: "Codice" reloadRequiredToApplySettings: "Per applicare le impostazioni, occorre ricaricare." +remainingN: "Rimangono: {n}" +overwriteContentConfirm: "Vuoi davvero sostituire l'attuale contenuto?" +seasonalScreenEffect: "Schermate in base alla stagione" +decorate: "Decora" _announcement: forExistingUsers: "Solo ai profili attuali" forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio." @@ -1618,6 +1627,7 @@ _role: canHideAds: "Nascondere i banner" canSearchNotes: "Ricercare nelle Note" canUseTranslator: "Tradurre le Note" + avatarDecorationLimit: "Numero massimo di decorazioni foto profilo installabili" _condition: isLocal: "Profilo locale" isRemote: "Profilo remoto" @@ -2123,6 +2133,7 @@ _profile: changeAvatar: "Modifica immagine profilo" changeBanner: "Cambia intestazione" verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo." + avatarDecorationMax: "Puoi aggiungere fino a {max} decorazioni." _exportOrImport: allNotes: "Tutte le note" favoritedNotes: "Note preferite" @@ -2239,8 +2250,6 @@ _notification: youGotReply: "{name} ti ha risposto" youGotQuote: "{name} ha citato la tua Nota e ha detto" youRenoted: "{name} ha rinotato" - youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" - youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat" youWereFollowed: "Adesso ti segue" youReceivedFollowRequest: "Hai ricevuto una richiesta di follow" yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 30171c6658..cedf92a9ec 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1,5 +1,9 @@ _lang_: "日本語" +welcome: "ようこそ!" +cherrypickMigrated: "CherryPickへの移行が完了しました!" +cherrypickMigratedCacheClearTitle: "キャッシュクリアのご案内" +cherrypickMigratedCacheClear: "このサーバーはMisskeyまたはCherryPick v4.3.0以前のバージョンから移行されました。\nバージョン管理方式が異なり、残っているキャッシュが問題を引き起こす可能性があるため、移行後、最初の接続時にキャッシュを削除する作業を行う必要があります。\n\nこの作業は最初一度だけ行われます。" showRenoteVisibilitySelector: "リノートの公開範囲オプションを表示" cannotBeUsedFunc: "この機能は現在使用できません。" scale: "大きさ" @@ -1878,6 +1882,7 @@ _emailUnavailable: disposable: "恒久的に使用可能なアドレスではありません" mx: "正しいメールサーバーではありません" smtp: "メールサーバーが応答しません" + banned: "このメールアドレスでは登録できません" _ffVisibility: public: "公開" @@ -2538,8 +2543,6 @@ _notification: youGotReply: "{name}からのリプライ" youGotQuote: "{name}による引用" youRenoted: "{name}がRenoteしました" - youGotMessagingMessageFromUser: "{name}からのチャットがあります" - youGotMessagingMessageFromGroup: "{name}のチャットがあります" youWereFollowed: "フォローされました" youReceivedFollowRequest: "フォローリクエストが来ました" yourFollowRequestAccepted: "フォローリクエストが承認されました" @@ -2547,6 +2550,7 @@ _notification: pollEnded: "アンケートの結果が出ました" newNote: "新しい投稿" unreadAntennaNote: "アンテナ {name}" + roleAssigned: "ロールが付与されました" emptyPushNotificationMessage: "プッシュ通知の更新をしました" achievementEarned: "実績を獲得" testNotification: "通知テスト" @@ -2570,6 +2574,7 @@ _notification: receiveFollowRequest: "フォロー申請を受け取った" followRequestAccepted: "フォローが受理された" groupInvited: "グループに招待された" + roleAssigned: "ロールが付与された" achievementEarned: "実績の獲得" app: "連携アプリからの通知" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 811e19faa0..990c1bfba0 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -121,7 +121,12 @@ sensitive: "気いつけて見いや" add: "増やす" reaction: "ツッコミ" reactions: "ツッコミ" -reactionSetting: "ピッカーに出しとくツッコミ" +emojiPicker: "絵文字ピッカー" +pinnedEmojisForReactionSettingDescription: "リアクションしたときにピンで留めてる表示をする絵文字を設定するで" +pinnedEmojisSettingDescription: "絵文字打ったときにピン留め表示する絵文字設定できるで" +emojiPickerDisplay: "ピッカーの表示" +overwriteFromPinnedEmojisForReaction: "リアクション設定から上書きする" +overwriteFromPinnedEmojis: "全般設定から上書きする" reactionSettingDescription2: "ドラッグで並び替え、クリックで削除、+を押して追加やで。" rememberNoteVisibility: "公開範囲覚えといて" attachCancel: "のっけるのやめる" @@ -261,6 +266,7 @@ removed: "ほかしたで!" removeAreYouSure: "「{x}」はほかしてええか?" deleteAreYouSure: "「{x}」はほかしてええか?" resetAreYouSure: "リセットしてええん?" +areYouSure: "いいん?" saved: "保存したで!" messaging: "チャット" upload: "アップロード" @@ -887,8 +893,6 @@ makeReactionsPublicDescription: "あんたがしたツッコミ一覧を誰で classic: "クラシック" muteThread: "スレッドをミュート" unmuteThread: "スレッドのミュートを解除" -ffVisibility: "つながりの公開範囲" -ffVisibilityDescription: "あんたのフォロー/フォロワー情報の公開範囲を設定できるで。" continueThread: "さらにスレッドを見るで" deleteAccountConfirm: "アカウントを消すで?ええんか?" incorrectPassword: "パスワードがちゃうわ。" @@ -1171,6 +1175,7 @@ tosAndPrivacyPolicy: "利用規約・プライバシーポリシー" avatarDecorations: "アイコンデコレーション" attach: "のっける" detach: "取る" +detachAll: "全部とる" angle: "角度" flip: "反転" showAvatarDecorations: "アイコンのデコレーション映す" @@ -1184,6 +1189,10 @@ cwNotationRequired: "「内容を隠す」んやったら注釈書かなアカ doReaction: "ツッコむで" code: "コード" reloadRequiredToApplySettings: "設定を見るんにはリロードが必要やで。" +remainingN: "残り:{n}" +overwriteContentConfirm: "今の内容に上書きされるけどいい?" +seasonalScreenEffect: "季節にあった画面の動き" +decorate: "デコる" _announcement: forExistingUsers: "もうおるユーザーのみ" forExistingUsersDescription: "オンにしたらこのお知らせができた時点でおる人らにだけお知らせが行くで。切ったらこの知らせが行ったあとにアカウント作った人にもちゃんとお知らせが行くで。" @@ -1618,6 +1627,7 @@ _role: canHideAds: "広告映さへん" canSearchNotes: "ノート探せるかどうか" canUseTranslator: "翻訳使えるかどうか" + avatarDecorationLimit: "アイコンデコのいっちばんつけれる数" _condition: isLocal: "ローカルユーザー" isRemote: "リモートユーザー" @@ -2123,6 +2133,7 @@ _profile: changeAvatar: "アバター画像を変更するで" changeBanner: "バナー画像を変更するで" verifiedLinkDescription: "内容をURLに設定すると、リンク先のwebサイトに自分のプロフのリンクが含まれてる場合に所有者確認済みアイコンを表示させることができるで。" + avatarDecorationMax: "最大{max}つまでデコつけれんで" _exportOrImport: allNotes: "全てのノート" favoritedNotes: "お気に入りにしたノート" @@ -2239,8 +2250,6 @@ _notification: youGotReply: "{name}からのリプライ" youGotQuote: "{name}による引用" youRenoted: "{name}がリノートしたみたいやで" - youGotMessagingMessageFromUser: "{name}からのチャットがあるで" - youGotMessagingMessageFromGroup: "{name}のチャットがあるで" youWereFollowed: "フォローされたで" youReceivedFollowRequest: "フォロー許可してほしいみたいやな" yourFollowRequestAccepted: "フォローさせてもろたで" diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml index e6128e1ffd..c974b10c98 100644 --- a/locales/ko-GS.yml +++ b/locales/ko-GS.yml @@ -15,7 +15,7 @@ gotIt: "알것어예" cancel: "아이예" noThankYou: "뎃어예" enterUsername: "사용자 이럼 서기" -renotedBy: "{user}님이 리노트햇십니다" +renotedBy: "{user}님이 리노트햇어예" noNotes: "노트가 없십니다" noNotifications: "알림이 없십니다" instance: "서버" @@ -76,7 +76,7 @@ export: "내가기" files: "파일" download: "내리받기" driveFileDeleteConfirm: "‘{name}’ 파일얼 뭉캡니꺼? 요 파일얼 서넌 콘텐츠도 뭉캐집니다." -unfollowConfirm: "{name}님얼 고만 팔로잉합니꺼?" +unfollowConfirm: "{name}님얼 고마 팔로잉합니꺼?" exportRequested: "내가기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다. 요청이 껕나모 ‘드라이브’에 옇십니다." importRequested: "가오기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다." lists: "리스트" @@ -113,7 +113,7 @@ cantReRenote: "리노트넌 지럴 리노트 몬 합니다." quote: "따오기" inChannelRenote: "채널 안 리노트" inChannelQuote: "채널 안 따오기" -pinnedNote: "프로필에 붙인 노트" +pinnedNote: "붙인 노트" pinned: "프로필에 붙이기" you: "나" clickToShow: "누질라서 보기" @@ -121,7 +121,6 @@ sensitive: "수ᇚ힛섭니다" add: "옇기" reaction: "반엉" reactions: "반엉" -reactionSetting: "모엄함서 포시할 반엉" reactionSettingDescription2: "꺼시서 두고, 누질라서 뭉캐고, ‘+’럴 누질라서 옇십니다." rememberNoteVisibility: "공개 범위럴 기억하기" attachCancel: "붙임 빼기" @@ -330,7 +329,7 @@ whenServerDisconnected: "서버하고 옌겔이 껂기모" disconnectedFromServer: "서버하고 옌겔이 껂깃십니다" reload: "새로곤침" doNothing: "무시하기" -reloadConfirm: "새로곤침합니까?" +reloadConfirm: "새로곤침합니꺼?" watch: "간심 갖기" unwatch: "간심 고마 갖기" accept: "받기" @@ -368,7 +367,7 @@ pinnedUsersDescription: "‘살펴보기’서 붙일라넌 사용자럴 줄 바 pinnedPages: "붙인 바닥" pinnedPagesDescription: "서버으 대문서 붙일라넌 바닥으 겡로럴 줄 바꿈해서로 적십니다." pinnedClipId: "붙일 클립으 아이디" -pinnedNotes: "프로필에 붙인 노트" +pinnedNotes: "붙인 노트" hcaptcha: "에이치캡차" enableHcaptcha: "에이치캡차 키기" hcaptchaSiteKey: "사이트키" @@ -381,7 +380,7 @@ turnstile: "턴스타일" enableTurnstile: "턴스타일 키기" turnstileSiteKey: "사이트키" turnstileSecretKey: "시크릿키" -avoidMultiCaptchaConfirm: "오만 캡차럴 서모 간섭이 잇얼 깁니다. 다린 캡차를 껍니까? ‘아이예’럴 누질리모 오만 캡차럴 키 둘 수도 잇십니다." +avoidMultiCaptchaConfirm: "오만 캡차럴 서모 간섭이 잇얼 깁니다. 다린 캡차를 껍니꺼? ‘아이예’럴 누질리모 오만 캡차럴 키 둘 수도 잇십니다." antennas: "안테나" manageAntennas: "안테나 간리" name: "이럼" @@ -413,7 +412,7 @@ userList: "리스트" about: "정보" aboutMisskey: "CherryPick넌예" administrator: "간리자" -token: "학인 코드" +token: "학인 기호" 2fa: "두 단게 정멩" setupOf2fa: "두 단게 정멩 설정" totp: "정멩 앱" @@ -426,13 +425,45 @@ moderationLogs: "중재 일지" nUsersMentioned: "{n}멩이 이바구하고 잇어예" securityKeyAndPasskey: "보안키·패스키" securityKey: "보안키" +unregister: "맨걸기 무루기" +share: "노누기" +notFound: "몬 찾앗십니다" +help: "도움말" invites: "초대하기" +retype: "다시 서기" +noteOf: "{user}님으 노트" invitations: "초대하기" +checking: "학인하고 잇십니다" +passwordMatched: "맞십니다" +passwordNotMatched: "안 맞십니다" language: "언어" +remote: "웬겍" +script: "스크립트" manage: "간리" +emailServer: "전자우펜 서버" +email: "전자우펜" +emailAddress: "전자우펜 주소" smtpHost: "호스트 이럼" +smtpPort: "포트" smtpUser: "사용자 이럼" smtpPass: "비밀번호" +abuseReports: "신고하기" +reportAbuse: "신고하기" +reportAbuseRenote: "리노트 신고하기" +reportAbuseOf: "{name}님얼 신고하기" +reporter: "신고한 사람" +reporteeOrigin: "신고덴 사람" +reporterOrigin: "신고한 곳" +forwardReport: "웬겍 서버에 신고 보내기" +random: "무작이" +system: "시스템" +clip: "클립 맨걸기" +notesCount: "노트 수" +renotesCount: "리노트한 수" +renotedCount: "리노트덴 수" +followingCount: "팔로우 수" +followersCount: "팔로워 수" +clips: "클립 맨걸기" clearCache: "캐시 비우기" unlikeConfirm: "좋네예럴 무룹니꺼?" info: "정보" @@ -440,6 +471,7 @@ user: "사용자" administration: "간리" on: "킴" off: "껌" +clickToFinishEmailVerification: "[{ok}]럴 누질라서 전자우펜 정멩얼 껕내이소." searchByGoogle: "찾기" tenMinutes: "십 분" oneHour: "한 시간" @@ -459,6 +491,20 @@ likeOnly: "좋네예마" icon: "아바타" replies: "답하기" renotes: "리노트" +_initialAccountSetting: + startTutorial: "길라잡이 하기" +_initialTutorial: + launchTutorial: "길라잡이 보기" + title: "길라잡이" + skipAreYouSure: "길라잡이럴 껕냅니까?" + _landing: + title: "길라잡이에 어서 오이소" + _done: + title: "길라잡이가 껕낫십니다!🎉" +_achievements: + _types: + _tutorialCompleted: + description: "길라잡이럴 껕냇십니다" _gallery: liked: "좋네예한 걸" like: "좋네예!" @@ -466,13 +512,16 @@ _gallery: _email: _follow: title: "새 팔로워가 잇십니다" +_channel: + removeBanner: "배너 뭉캐기" _theme: keys: mention: "멘션" _sfx: - note: "노트" + note: "새 노트" notification: "알림" _2fa: + step3Title: "학인 기호럴 서기" renewTOTPCancel: "뎃어예" _widgets: profile: "프로필" @@ -501,11 +550,15 @@ _charts: federation: "옌합" _timelines: home: "덜머리" +_play: + script: "스크립트" _pages: like: "좋네예" unlike: "좋네예 무루기" blocks: image: "이미지" + _note: + id: "노트 아이디" _notification: youWereFollowed: "새 팔로워가 잇십니다" _types: @@ -526,3 +579,6 @@ _webhookSettings: name: "이럼" _moderationLogTypes: suspend: "얼우기" + deleteNote: "노트 뭉캐기" + deleteUserAnnouncement: "사용자 공지 걸 뭉캐기" + resolveAbuseReport: "신고 해겔하기" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index f374df91c5..0920a34774 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1,5 +1,9 @@ --- _lang_: "한국어" +welcome: "환영합니다!" +cherrypickMigrated: "CherryPick으로 마이그레이션이 완료되었어요!" +cherrypickMigratedCacheClearTitle: "캐시 삭제 안내" +cherrypickMigratedCacheClear: "이 서버는 Misskey 또는 CherryPick v4.3.0 이전 버전에서 마이그레이션 되었어요.\n버전 관리 방식이 다르기 때문에 남아있는 캐시가 문제를 일으킬 수 있으므로, 마이그레이션 이후 첫 접속 시 캐시를 삭제하는 작업을 진행해야 해요.\n\n이 작업은 처음 한 번만 진행돼요." showRenoteVisibilitySelector: "리노트 공개 범위 옵션 표시" cannotBeUsedFunc: "지금은 사용할 수 없는 기능입니다." scale: "크기" @@ -190,7 +194,12 @@ sensitive: "열람 주의" add: "추가하기" reaction: "리액션" reactions: "리액션" -reactionSetting: "선택기에 표시할 리액션" +emojiPicker: "이모지 선택기" +pinnedEmojisForReactionSettingDescription: "리액션을 할 때 선택기에 고정해서 표시할 이모지" +pinnedEmojisSettingDescription: "이모지를 입력할 때 선택기에 고정해서 표시할 이모지" +emojiPickerDisplay: "선택기 표시" +overwriteFromPinnedEmojisForReaction: "리액션 설정 덮어쓰기" +overwriteFromPinnedEmojis: "일반 설정 덮어쓰기" reactionSettingDescription2: "끌어서 순서 변경, 클릭해서 삭제, +를 눌러서 추가할 수 있습니다." rememberNoteVisibility: "공개 범위 기억하기" attachCancel: "파일 첨부 취소하기" @@ -330,6 +339,7 @@ removed: "삭제되었습니다." removeAreYouSure: "\"{x}\"을(를) 삭제하시겠습니까?" deleteAreYouSure: "\"{x}\"을(를) 삭제하시겠습니까?" resetAreYouSure: "정말 초기화히시겠습니까?" +areYouSure: "계속하시겠습니까?" saved: "저장됨!" messaging: "메시지" upload: "업로드하기" @@ -633,7 +643,7 @@ showInPage: "페이지로 보기" popout: "새 창으로 열기" volume: "음량" masterVolume: "마스터 볼륨" -notUseSound: "사운드를 출력하지 않음" +notUseSound: "사운드 출력 비활성화" useSoundOnlyWhenActive: "CherryPick이 활성화된 경우에만 사운드 출력" details: "자세히" chooseEmoji: "이모지 선택" @@ -775,7 +785,7 @@ defaultNavigationBehaviour: "기본 탐색 동작" editTheseSettingsMayBreakAccount: "이 설정을 변경하면 계정이 손상될 수 있으니 주의해 주세요!" instanceTicker: "노트의 서버 정보" waitingFor: "{x}을(를) 기다리고 있어요" -random: "랜덤" +random: "무작위" system: "시스템" switchUi: "UI 전환" desktop: "데스크탑" @@ -967,8 +977,8 @@ makeReactionsPublicDescription: "나의 리액션을 누구나 볼 수 있게 classic: "클래식" muteThread: "이 대화를 뮤트" unmuteThread: "이 대화 뮤트 해제" -ffVisibility: "내 인맥의 공개 범위" -ffVisibilityDescription: "나의 팔로우와 팔로워 정보에 대한 공개 범위를 설정할 수 있습니다." +followingVisibility: "팔로우 목록의 공개 범위" +followersVisibility: "팔로워 목록의 공개 범위" continueThread: "대화 이어서 보기" deleteAccountConfirm: "계정이 삭제되고 복구할 수 없습니다. 그래도 계속하시겠습니까?" incorrectPassword: "올바르지 않은 계정 정보입니다." @@ -1264,7 +1274,8 @@ privacyPolicyUrl: "개인정보 보호 정책 URL" tosAndPrivacyPolicy: "약관 및 개인정보 보호 정책" avatarDecorations: "아바타 장식" attach: "붙이기" -detach: "떼기" +detach: "제거" +detachAll: "모두 제거" angle: "각도" flip: "플립" showAvatarDecorations: "아바타 장식 표시" @@ -1278,6 +1289,10 @@ cwNotationRequired: "'내용 숨기기'를 체크했을 경우 내용에 대한 doReaction: "리액션 추가" code: "코드" reloadRequiredToApplySettings: "설정을 반영하려면 페이지를 다시 불러와야 해요." +remainingN: "남음: {n}" +overwriteContentConfirm: "현재 내용을 덮어쓰기 하게 돼요. 그래도 계속 진행할까요?" +seasonalScreenEffect: "계절에 따른 화면 연출" +decorate: "장식하기" showUnreadNotificationsCount: "읽지 않은 알림 수 표시" showCatOnly: "고양이만 보기" additionalPermissionsForFlash: "Play에 대한 추가 권한" @@ -1828,6 +1843,7 @@ _role: canHideAds: "광고 숨기기" canSearchNotes: "노트 검색 이용 가능 여부" canUseTranslator: "노트 번역 이용 가능 여부" + avatarDecorationLimit: "붙일 수 있는 아바타 장식의 최대 개수" _condition: isLocal: "로컬 유저" isRemote: "리모트 유저" @@ -2142,7 +2158,7 @@ _sfx: channel: "채널 알림" reaction: "리액션 선택" _soundSettings: - driveFile: "드라이브에 있는 오디오 사용" + driveFile: "드라이브에 있는 오디오 파일 사용" driveFileWarn: "드라이브에 있는 파일을 선택해 주세요." driveFileTypeWarn: "이 파일은 지원되지 않는 형식이에요." driveFileTypeWarnDescription: "오디오 파일을 선택해 주세요." @@ -2357,6 +2373,7 @@ _profile: changeAvatar: "아바타 이미지 변경" changeBanner: "배너 이미지 변경" verifiedLinkDescription: "내용에 자신의 프로필로 향하는 링크가 포함된 페이지의 URL을 삽입하면 소유자 인증 마크가 표시돼요." + avatarDecorationMax: "최대 {max}개까지 장식을 달 수 있어요." _exportOrImport: allNotes: "모든 노트" favoritedNotes: "즐겨찾기한 노트" @@ -2467,7 +2484,7 @@ _relayStatus: accepted: "승인됨" rejected: "거절됨" _notification: - fileUploaded: "파일 업로드가 완료되었습니다!" + fileUploaded: "파일 업로드가 완료됨" youGotMention: "{name}님이 멘션함" youGotReply: "{name}님이 답글을 남김" youGotQuote: "{name}님이 인용함" @@ -2481,6 +2498,7 @@ _notification: newNote: "새로운 노트" pollEnded: "참여한 투표의 결과가 공개됨" unreadAntennaNote: "안테나 {name}의 새 노트" + roleAssigned: "새로운 역할에 할당됨" emptyPushNotificationMessage: "새로운 알림이 있습니다." achievementEarned: "도전 과제를 달성함" testNotification: "알림 테스트" @@ -2500,9 +2518,10 @@ _notification: quote: "인용" reaction: "리액션" pollEnded: "투표가 종료됨" - receiveFollowRequest: "팔로우 요청을 받았을 때" - followRequestAccepted: "팔로우 요청이 승인되었을 때" - groupInvited: "그룹에 초대되었을 때" + receiveFollowRequest: "팔로우 요청을 받음" + followRequestAccepted: "팔로우 요청이 승인됨" + groupInvited: "그룹에 초대됨" + roleAssigned: "역할이 할당됨" achievementEarned: "도전 과제 획득" app: "연동된 앱을 통한 알림" _actions: diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 9b69155683..a29e3738a8 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -119,7 +119,6 @@ sensitive: "NSFW" add: "Toevoegen" reaction: "Reacties" reactions: "Reacties" -reactionSetting: "Reacties die in de reactie-selector worden getoond" reactionSettingDescription2: "Sleep om opnieuw te ordenen, Klik om te verwijderen, Druk op \"+\" om toe te voegen" rememberNoteVisibility: "Vergeet niet de notitie zichtbaarheidsinstellingen" attachCancel: "Verwijder bijlage" diff --git a/locales/no-NO.yml b/locales/no-NO.yml index d3fa5d2fd7..c21bf65ced 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -102,7 +102,6 @@ clickToShow: "Klikk for å vise" add: "Legg til" reaction: "Reaksjon" reactions: "Reaksjoner" -reactionSetting: "Reaksjoner som vises i reaksjonsvelgeren" reactionSettingDescription2: "Dra for å endre rekkefølgen, klikk for å slette, trykk \"+\" for å legge til." rememberNoteVisibility: "Husk innstillingene for synlighet av Notes" attachCancel: "Fjern vedlegg" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 5b02268de8..94afb06a9f 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -111,7 +111,6 @@ sensitive: "NSFW" add: "Dodaj" reaction: "Reakcja" reactions: "Reakcja" -reactionSetting: "Reakcje do pokazania w wyborniku reakcji" reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+” aby dodać" rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu" attachCancel: "Usuń załącznik" @@ -819,8 +818,6 @@ makeReactionsPublicDescription: "To spowoduje, że lista wszystkich Twoich dotyc classic: "Klasyczny" muteThread: "Wycisz wątek" unmuteThread: "Wyłącz wyciszenie wątku" -ffVisibility: "Widoczność obserwowanych/obserwujących" -ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz i kto Cię obserwuje." continueThread: "Pokaż kontynuację wątku" deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie Twojego konta. Kontynuować?" incorrectPassword: "Nieprawidłowe hasło." @@ -1412,8 +1409,6 @@ _notification: youGotReply: "{name} odpowiedział(a) Tobie" youGotQuote: "{name} zacytował(a) Ciebie" youRenoted: "{name} udostępnił(a) Twój wpis" - youGotMessagingMessageFromUser: "{name} wysłał(a) Ci wiadomość" - youGotMessagingMessageFromGroup: "Została wysłana wiadomość do grupy {name}" youWereFollowed: "Zaobserwował(a) Cię" youReceivedFollowRequest: "Otrzymałeś(-aś) prośbę o możliwość obserwacji" yourFollowRequestAccepted: "Twoja prośba o możliwość obserwacji została przyjęta" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 7ec05ca6fe..87c78340cb 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -121,7 +121,6 @@ sensitive: "Conteúdo sensível" add: "Adicionar" reaction: "Reações" reactions: "Reações" -reactionSetting: "Quais reações exibir no seletor de reações" reactionSettingDescription2: "Arraste para reordenar, clique para excluir, pressione + para adicionar." rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas" attachCancel: "Remover anexo" @@ -859,8 +858,6 @@ makeReactionsPublicDescription: "Isto vai deixar o histórico de todas as suas r classic: "Clássico" muteThread: "Silenciar esta conversa" unmuteThread: "Desativar silêncio desta conversa" -ffVisibility: "Visibilidade de Seguidos/Seguidores" -ffVisibilityDescription: "Permite configurar quem pode ver quem lhe segue e quem você está seguindo." continueThread: "Ver mais desta conversa" deleteAccountConfirm: "Deseja realmente excluir a conta?" incorrectPassword: "Senha inválida." diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 5004308219..7f6e64cd6d 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -121,7 +121,6 @@ sensitive: "NSFW" add: "Adaugă" reaction: "Reacție" reactions: "Reacție" -reactionSetting: "Reacții care să apară in selectorul de reacții" reactionSettingDescription2: "Trage pentru a rearanja, apasă pe \"+\" pentru a adăuga." rememberNoteVisibility: "Amintește setarea de vizibilitate a notelor" attachCancel: "Înlătură atașament" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 1cf03fdd8d..8cdc9ed9b0 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -120,7 +120,6 @@ sensitive: "Содержимое не для всех" add: "Добавить" reaction: "Реакции" reactions: "Реакции" -reactionSetting: "Реакции, отображаемые в палитре" reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте кнопкой «+»." rememberNoteVisibility: "Запоминать видимость заметок" attachCancel: "Удалить вложение" @@ -869,8 +868,6 @@ makeReactionsPublicDescription: "Список сделанных вами реа classic: "Классика" muteThread: "Скрыть цепочку" unmuteThread: "Отменить сокрытие цепочки" -ffVisibility: "Видимость подписок и подписчиков" -ffVisibilityDescription: "Здесь можно настроить, кто будет видеть ваши подписки и подписчиков." continueThread: "Показать следующие ответы" deleteAccountConfirm: "Учётная запись будет безвозвратно удалена. Подтверждаете?" incorrectPassword: "Пароль неверен." @@ -1977,8 +1974,6 @@ _notification: youGotReply: "{name} отвечает вам." youGotQuote: "{name} цитирует вас." youRenoted: "{name} передаёт вашу заметку." - youGotMessagingMessageFromUser: "{name} пишет вам." - youGotMessagingMessageFromGroup: "Новое сообщение в группе «{name}»." youWereFollowed: "У вас новый подписчик." youReceivedFollowRequest: "У вас новый запрос на подписку." yourFollowRequestAccepted: "Ваш запрос на подписку одобрен." diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 99edcbec76..e84bf3bab0 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -113,7 +113,6 @@ sensitive: "NSFW" add: "Pridať" reaction: "Reakcie" reactions: "Reakcie" -reactionSetting: "Reakcie zobrazené vo výbere reakcií" reactionSettingDescription2: "Ťahaním preusporiadate, kliknutím odstránite, Stlačením \"+\" pridáte" rememberNoteVisibility: "Zapamätať nastavenia viditeľnosti poznámky" attachCancel: "Odstrániť prílohu" @@ -834,8 +833,6 @@ makeReactionsPublicDescription: "Toto spraví všetky vaše minulé reakcie vidi classic: "Klasika" muteThread: "Ztíšiť vlákno" unmuteThread: "Zrušiť stíšenie vlákna" -ffVisibility: "Viditeľnosť sledujúcich/sledovaných" -ffVisibilityDescription: "Umožňuje nastaviť kto vidí koho sledujete a kto vás sleduje." continueThread: "Zobraziť pokračovanie vlákna" deleteAccountConfirm: "Toto nezvrátiteľne vymaže váš účet. Pokračovať?" incorrectPassword: "Nesprávne heslo." @@ -1473,8 +1470,6 @@ _notification: youGotReply: "{name} vám odpovedal/a" youGotQuote: "{name} vás citoval/a" youRenoted: "{name} preposlal/a vašu poznámku" - youGotMessagingMessageFromUser: "{name} vám poslal/a správu" - youGotMessagingMessageFromGroup: "Prišla správa do skupiny {name}" youWereFollowed: "Máte nového sledujúceho" youReceivedFollowRequest: "Dostali ste žiadosť o sledovanie" yourFollowRequestAccepted: "Vaša žiadosť o sledovanie bola prijatá" diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index c8537c68a1..87064f78bc 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -118,7 +118,6 @@ sensitive: "Känsligt innehåll" add: "Lägg till" reaction: "Reaktioner" reactions: "Reaktioner" -reactionSetting: "Reaktioner som ska visas i reaktionsväljaren" reactionSettingDescription2: "Dra för att omordna, klicka för att radera, tryck \"+\" för att lägga till." rememberNoteVisibility: "Komihåg notvisningsinställningar" attachCancel: "Ta bort bilaga" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index c28617e472..dfbe4ce63f 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -121,7 +121,6 @@ sensitive: "เนื้อหาที่ละเอียดอ่อน NSFW add: "เพิ่ม" reaction: "รีแอคชั่น" reactions: "รีแอคชั่น" -reactionSetting: "รีแอคชั่นไปยังแสดงผลในตัวเลือกการรีแอคชั่น" reactionSettingDescription2: "กดลากเพื่อจัดลำดับใหม่ กดคลิกเพื่อลบ กด \"+\" เพื่อเพิ่ม" rememberNoteVisibility: "จดจำการตั้งค่าการมองเห็นตัวโน้ต" attachCancel: "ลบไฟล์ออกที่แนบมา" @@ -882,8 +881,6 @@ makeReactionsPublicDescription: "การทำเช่นนี้จะท classic: "คลาสสิค" muteThread: "ปิดเสียงเธรด" unmuteThread: "เปิดเสียงเธรด" -ffVisibility: "การมองเห็นผู้ติดตาม/ผู้ติดตาม" -ffVisibilityDescription: "ช่วยให้คุณสามารถกำหนดค่าได้ว่าใครสามารถดูได้ว่าคุณติดตามใครและใครติดตามคุณบ้าง" continueThread: "ดูความต่อเนื่องเธรด" deleteAccountConfirm: "การดำเนินการนี้จะลบบัญชีของคุณอย่างถาวรเลยนะ แน่ใจหรอดำเนินการ?" incorrectPassword: "รหัสผ่านไม่ถูกต้อง" @@ -2139,8 +2136,6 @@ _notification: youGotReply: "{name} ตอบกลับถึงคุณ" youGotQuote: "{name} อ้างถึงคุณ" youRenoted: "รีโน้ตจาก {name}" - youGotMessagingMessageFromUser: "{name} ได้ส่งข้อความแชทถึงคุณ" - youGotMessagingMessageFromGroup: "ข้อความแชทถูกส่งไปยัง {name} กลุ่ม" youWereFollowed: "ได้ติดตามคุณ" youReceivedFollowRequest: "คุณมีคำขอติดตามใหม่น่ะ" yourFollowRequestAccepted: "คำขอติดตามของคุณได้รับการยอมรับแล้วน่ะ" diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index 8cad592738..120b9f4230 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -121,7 +121,6 @@ sensitive: "Hassas içerik" add: "Ekle" reaction: "Tepkiler" reactions: "Tepkiler" -reactionSetting: "Palette görünecek tepkiler" reactionSettingDescription2: "Sıralamak için sürükleyin, silmek için tıklayın, eklemek için \"+\" tuşuna tıklayın." rememberNoteVisibility: "Görünürlük ayarlarını hatırla" attachCancel: "Eki sil" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 09c6a7f33c..73ce06a155 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -55,6 +55,7 @@ copyRSS: "Скопіювати RSS" copyUsername: "Скопіювати ім’я користувача" copyUserId: "Копіювати ID користувача" copyNoteId: "блокнот ID користувача" +copyFileId: "Скопіювати ідентифікатор файлу." searchUser: "Пошук користувачів" reply: "Відповісти" loadMore: "Показати більше" @@ -115,7 +116,6 @@ sensitive: "NSFW" add: "Додати" reaction: "Реакції" reactions: "Реакції" -reactionSetting: "Налаштування реакцій" reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати." rememberNoteVisibility: "Пам’ятати параметри видимісті" attachCancel: "Видалити вкладення" @@ -133,6 +133,7 @@ unblockConfirm: "Ви впевнені, що хочете розблокуват suspendConfirm: "Ви впевнені, що хочете призупинити цей акаунт?" unsuspendConfirm: "Ви впевнені, що хочете відновити цей акаунт?" selectList: "Виберіть список" +editList: "Редагувати список." selectChannel: "Виберіть канал" selectAntenna: "Виберіть антену" selectWidget: "Виберіть віджет" @@ -458,6 +459,7 @@ language: "Мова" uiLanguage: "Мова інтерфейсу" groupInvited: "Запрошення до групи" aboutX: "Про {x}" +native: "місцевий" disableDrawer: "Не використовувати висувні меню" youHaveNoGroups: "Немає груп" joinOrCreateGroup: "Отримуйте запрошення до груп або створюйте свої власні групи." @@ -538,6 +540,8 @@ output: "Вихід" script: "Скрипт" disablePagesScript: "Вимкнути AiScript на Сторінках" updateRemoteUser: "Оновити інформацію про віддаленого користувача" +unsetUserAvatar: "Деактивувати піктограму." +unsetUserBanner: "Випустити прапор." deleteAllFiles: "Видалити всі файли" deleteAllFilesConfirm: "Ви дійсно хочете видалити всі файли?" removeAllFollowing: "Скасувати всі підписки" @@ -825,7 +829,6 @@ makeReactionsPublicDescription: "Це зробить список усіх ва classic: "Класичний" muteThread: "Приглушити тред" unmuteThread: "Скасувати глушіння" -ffVisibility: "Видимість підписок/підписників" continueThread: "Показати продовження треду" deleteAccountConfirm: "Це незворотно видалить ваш акаунт. Продовжити?" incorrectPassword: "Неправильний пароль." @@ -1643,8 +1646,6 @@ _notification: youGotReply: "{name} відповідає" youGotQuote: "{name} цитує вас" youRenoted: "{name} поширює" - youGotMessagingMessageFromUser: "Повідомлення від {name}" - youGotMessagingMessageFromGroup: "Нове повідомлення в групі {name}" youWereFollowed: "Новий підписник" youReceivedFollowRequest: "Ви отримали запит на підписку" yourFollowRequestAccepted: "Запит на підписку прийнято" diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml index 6b6997010a..162eef6fa8 100644 --- a/locales/uz-UZ.yml +++ b/locales/uz-UZ.yml @@ -120,7 +120,6 @@ sensitive: "Sezuvchan" add: "Qo'shish" reaction: "Reaktsiyalar" reactions: "Reaktsiyalar" -reactionSetting: "Reaksiyalar ro'yxati" reactionSettingDescription2: "Qayta tartiblash uchun ushlab turib siljiting, oʻchirish uchun bosing, qoʻshish uchun “+” tugmasini bosing." rememberNoteVisibility: "Qaydning ko'rinish sozlamarini eslab qolish" attachCancel: "Qo'shimchani olib tashlash" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index f746979150..1c4313aef5 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -121,7 +121,6 @@ sensitive: "Nhạy cảm" add: "Thêm" reaction: "Biểu cảm" reactions: "Biểu cảm" -reactionSetting: "Chọn những biểu cảm hiển thị" reactionSettingDescription2: "Kéo để sắp xếp, nhấn để xóa, nhấn \"+\" để thêm." rememberNoteVisibility: "Lưu kiểu tút mặc định" attachCancel: "Gỡ tập tin đính kèm" @@ -870,8 +869,6 @@ makeReactionsPublicDescription: "Điều này sẽ hiển thị công khai danh classic: "Cổ điển" muteThread: "Không quan tâm nữa" unmuteThread: "Quan tâm tút này" -ffVisibility: "Hiển thị Theo dõi/Người theo dõi" -ffVisibilityDescription: "Quyết định ai có thể xem những người bạn theo dõi và những người theo dõi bạn." continueThread: "Tiếp tục xem chuỗi tút" deleteAccountConfirm: "Điều này sẽ khiến tài khoản bị xóa vĩnh viễn. Vẫn tiếp tục?" incorrectPassword: "Sai mật khẩu." @@ -1870,8 +1867,6 @@ _notification: youGotReply: "{name} trả lời bạn" youGotQuote: "{name} trích dẫn tút của bạn" youRenoted: "{name} đăng lại tút của bạn" - youGotMessagingMessageFromUser: "{name} nhắn tin cho bạn" - youGotMessagingMessageFromGroup: "Một tin nhắn trong nhóm {name}" youWereFollowed: "đã theo dõi bạn" youReceivedFollowRequest: "Bạn vừa có một yêu cầu theo dõi" yourFollowRequestAccepted: "Yêu cầu theo dõi của bạn đã được chấp nhận" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index c1ad1b2aa9..c547b7b00d 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -121,7 +121,6 @@ sensitive: "敏感内容" add: "添加" reaction: "回应" reactions: "回应" -reactionSetting: "在选择器中显示回应" reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。" rememberNoteVisibility: "保存上次设置的可见性" attachCancel: "删除附件" @@ -879,8 +878,6 @@ makeReactionsPublicDescription: "将您发表过的回应设置成公开可见 classic: "经典" muteThread: "屏蔽帖子列表" unmuteThread: "取消屏蔽帖子列表" -ffVisibility: "关注关系的可见范围" -ffVisibilityDescription: "您可以设置您的关注/关注者信息的公开范围" continueThread: "查看更多帖子" deleteAccountConfirm: "将要删除账户。是否确认?" incorrectPassword: "密码错误" @@ -1178,7 +1175,7 @@ _serverSettings: appIconUsageExample: "例如:作为书签添加到 PWA 或手机主屏幕的时候" appIconStyleRecommendation: "因为有可能会被裁切为圆形或者圆角矩形,建议使用边缘带有留白背景的图标。" appIconResolutionMustBe: "分辨率必须为 {resolution}。" - manifestJsonOverride: "覆盖 mainfest.json" + manifestJsonOverride: "覆盖 manifest.json" shortName: "简称" shortNameDescription: "如果服务器的正式名称很长,可以用简称或者別名来替代。" _accountMigration: @@ -2099,8 +2096,6 @@ _notification: youGotReply: "来自{name}的回复" youGotQuote: "来自{name}的引用" youRenoted: "来自{name}的转发" - youGotMessagingMessageFromUser: "来自{name}的聊天" - youGotMessagingMessageFromGroup: "来自{name}的群聊" youWereFollowed: "关注了你。" youReceivedFollowRequest: "您有新的关注请求" yourFollowRequestAccepted: "您的关注请求已通过" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index c45d91050d..652722db05 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -121,7 +121,12 @@ sensitive: "敏感內容" add: "新增" reaction: "反應" reactions: "反應" -reactionSetting: "在選擇器中顯示反應" +emojiPicker: "表情符號選擇器" +pinnedEmojisForReactionSettingDescription: "選擇反應時可以設定要固定顯示在頂端的表情符號" +pinnedEmojisSettingDescription: "輸入表情符號時可以設定要固定顯示在頂端的表情符號" +emojiPickerDisplay: "顯示表情符號選擇器" +overwriteFromPinnedEmojisForReaction: "從反應複寫設定" +overwriteFromPinnedEmojis: "從一般複寫設定" reactionSettingDescription2: "拖動以交換,點擊以刪除,按下「+」以新增。" rememberNoteVisibility: "記住貼文可見性" attachCancel: "移除附件" @@ -261,7 +266,7 @@ removed: "已刪除" removeAreYouSure: "確定要刪掉「{x}」嗎?" deleteAreYouSure: "確定要刪掉「{x}」嗎?" resetAreYouSure: "確定要重設嗎?" -areYouSure: "您確定要移除所有裝飾嗎?" +areYouSure: "是否確定?" saved: "已儲存" messaging: "聊天" upload: "上傳" @@ -794,7 +799,7 @@ receiveAnnouncementFromInstance: "接收由本實例發出的電郵通知" emailNotification: "郵件通知" publish: "發布" inChannelSearch: "頻道内搜尋" -useReactionPickerForContextMenu: "點擊右鍵開啟反應工具欄" +useReactionPickerForContextMenu: "點擊右鍵開啟反應選擇器" typingUsers: "{users}輸入中" jumpToSpecifiedDate: "跳轉到特定日期" showingPastTimeline: "顯示過往的時間軸" @@ -888,8 +893,8 @@ makeReactionsPublicDescription: "將您做過的反應設為公開可見。" classic: "經典" muteThread: "將貼文串設為靜音" unmuteThread: "將貼文串的靜音解除" -ffVisibility: "連繫的可見性" -ffVisibilityDescription: "您可以設定追隨或追隨者資訊的公開範圍" +followingVisibility: "追隨中的可見性" +followersVisibility: "追隨者的可見性" continueThread: "查看更多貼文" deleteAccountConfirm: "將要刪除帳戶。是否確定?" incorrectPassword: "密碼錯誤。" @@ -1187,6 +1192,9 @@ doReaction: "做出反應" code: "程式碼" reloadRequiredToApplySettings: "需要重新載入頁面設定才能生效。" remainingN: "剩餘:{n}" +overwriteContentConfirm: "確定要覆蓋目前的內容嗎?" +seasonalScreenEffect: "隨季節變換畫面的呈現" +decorate: "設置頭像裝飾" _announcement: forExistingUsers: "僅限既有的使用者" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" @@ -1223,7 +1231,7 @@ _initialTutorial: skipAreYouSure: "結束教學模式?" _landing: title: "歡迎使用本教學課程" - description: "在這裡您可以查看CherryPick的基本使用方法和功能。" + description: "在這裡您可以查看 CherryPick 的基本使用方法和功能。" _note: title: "什麼是貼文?" description: "在CherryPick上發布的內容稱為「貼文」。貼文在時間軸上按時間順序排列,並即時更新。" @@ -2128,7 +2136,7 @@ _profile: changeAvatar: "更換大頭貼" changeBanner: "變更橫幅圖像" verifiedLinkDescription: "如果輸入包含您個人資料的網站 URL,欄位旁邊將出現驗證圖示。" - avatarDecorationMax: "最多可以設置{max}個裝飾。" + avatarDecorationMax: "最多可以設置 {max} 個裝飾。" _exportOrImport: allNotes: "所有貼文" favoritedNotes: "「我的最愛」貼文" @@ -2245,8 +2253,6 @@ _notification: youGotReply: "{name}回覆了您" youGotQuote: "{name}引用了您" youRenoted: "{name} 轉發了你的貼文" - youGotMessagingMessageFromUser: "{name}發送給您的訊息" - youGotMessagingMessageFromGroup: "{name}發送給您的訊息" youWereFollowed: "您有新的追隨者" youReceivedFollowRequest: "您有新的追隨請求" yourFollowRequestAccepted: "您的追隨請求已通過" @@ -2254,6 +2260,7 @@ _notification: pollEnded: "問卷調查已產生結果" newNote: "新的貼文" unreadAntennaNote: "天線 {name}" + roleAssigned: "已授予角色" emptyPushNotificationMessage: "推送通知已更新" achievementEarned: "獲得成就" testNotification: "通知測試" @@ -2276,6 +2283,7 @@ _notification: receiveFollowRequest: "已收到追隨請求" followRequestAccepted: "追隨請求已接受" groupInvited: "加入社群邀請" + roleAssigned: "已授予角色" achievementEarned: "獲得成就" app: "應用程式通知" _actions: diff --git a/package.json b/package.json index 730436151b..a066f7fbf7 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "lycheebridge", - "version": "2023.12.4-beta.1", - "basedCherryPickVersion": "4.6.0-beta.5", - "basedMisskeyVersion": "2023.12.0-beta.5", + "version": "2023.12.4-beta.2", + "basedCherryPickVersion": "4.6.0-beta.6", + "basedMisskeyVersion": "2023.12.0", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/migration/1681429921400-Event.js b/packages/backend/migration/1681429921400-Event.js index 3c9e35c6dc..2cd2657a12 100644 --- a/packages/backend/migration/1681429921400-Event.js +++ b/packages/backend/migration/1681429921400-Event.js @@ -15,7 +15,6 @@ export class Event1681429921400 { await queryRunner.query(`COMMENT ON COLUMN "user"."isRoot" IS 'Whether the User is the root.'`); await queryRunner.query(`COMMENT ON COLUMN "ad"."startsAt" IS 'The expired date of the Ad.'`); await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "lastUsedAt" DROP DEFAULT`); - await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS 'The created date of the Muting.'`); await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS 'The mutee user ID.'`); await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS 'The muter user ID.'`); await queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b"`); diff --git a/packages/backend/migration/1703209889304-bannedEmailDomains.js b/packages/backend/migration/1703209889304-bannedEmailDomains.js new file mode 100644 index 0000000000..8d7f09e22e --- /dev/null +++ b/packages/backend/migration/1703209889304-bannedEmailDomains.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class bannedEmailDomains1703209889304 { + constructor() { + this.name = 'bannedEmailDomains1703209889304'; + } + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "bannedEmailDomains" character varying(1024) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "bannedEmailDomains"`); + } +} diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 55a6d955df..63c7c9b797 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { URLSearchParams } from 'node:url'; import * as nodemailer from 'nodemailer'; import { Inject, Injectable } from '@nestjs/common'; import { validate as validateEmail } from 'deep-email-validator'; -import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js'; import { MetaService } from '@/core/MetaService.js'; +import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -30,6 +29,7 @@ export class EmailService { private metaService: MetaService, private loggerService: LoggerService, + private utilityService: UtilityService, private httpRequestService: HttpRequestService, ) { this.logger = this.loggerService.getLogger('email'); @@ -155,7 +155,7 @@ export class EmailService { @bindThis public async validateEmailForAccount(emailAddress: string): Promise<{ available: boolean; - reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; + reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned'; }> { const meta = await this.metaService.fetch(); @@ -164,32 +164,35 @@ export class EmailService { email: emailAddress, }); - const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null; let validated; - if (meta.enableActiveEmailValidation && meta.verifymailAuthKey) { - if (verifymailApi) { + if (meta.enableActiveEmailValidation) { + if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) { validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey); } else { - validated = meta.enableActiveEmailValidation ? await validateEmail({ + validated = await validateEmail({ email: emailAddress, validateRegex: true, validateMx: true, validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので validateDisposable: true, // 捨てアドかどうかチェック validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので - }) : { valid: true, reason: null }; + }); } } else { validated = { valid: true, reason: null }; } - const available = exist === 0 && validated.valid; + const emailDomain: string = emailAddress.split('@')[1]; + const isBanned = this.utilityService.isBlockedHost(meta.bannedEmailDomains, emailDomain); + + const available = exist === 0 && validated.valid && !isBanned; return { available, reason: available ? null : exist !== 0 ? 'used' : + isBanned ? 'banned' : validated.reason === 'regex' ? 'format' : validated.reason === 'disposable' ? 'disposable' : validated.reason === 'mx' ? 'mx' : diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts index a6fbb0a927..ff45a601c7 100644 --- a/packages/backend/src/core/FeaturedService.ts +++ b/packages/backend/src/core/FeaturedService.ts @@ -77,6 +77,17 @@ export class FeaturedService { return Array.from(ranking.keys()); } + @bindThis + private async removeFromRanking(name: string, windowRange: number, element: string): Promise { + const currentWindow = this.getCurrentWindow(windowRange); + const previousWindow = currentWindow - 1; + + const redisPipeline = this.redisClient.pipeline(); + redisPipeline.zrem(`${name}:${currentWindow}`, element); + redisPipeline.zrem(`${name}:${previousWindow}`, element); + await redisPipeline.exec(); + } + @bindThis public updateGlobalNotesRanking(noteId: MiNote['id'], score = 1): Promise { return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score); @@ -126,4 +137,9 @@ export class FeaturedService { public getHashtagsRanking(threshold: number): Promise { return this.getRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, threshold); } + + @bindThis + public removeHashtagsFromRanking(hashtag: string): Promise { + return this.removeFromRanking('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, hashtag); + } } diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index c6aa4732f3..464c53e1f2 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -11,6 +11,7 @@ import { MiMeta } from '@/models/Meta.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; +import { FeaturedService } from '@/core/FeaturedService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -25,6 +26,7 @@ export class MetaService implements OnApplicationShutdown { @Inject(DI.db) private db: DataSource, + private featuredService: FeaturedService, private globalEventService: GlobalEventService, ) { //this.onMessage = this.onMessage.bind(this); @@ -95,6 +97,8 @@ export class MetaService implements OnApplicationShutdown { @bindThis public async update(data: Partial): Promise { + let before: MiMeta | undefined; + const updated = await this.db.transaction(async transactionalEntityManager => { const metas = await transactionalEntityManager.find(MiMeta, { order: { @@ -102,10 +106,10 @@ export class MetaService implements OnApplicationShutdown { }, }); - const meta = metas[0]; + before = metas[0]; - if (meta) { - await transactionalEntityManager.update(MiMeta, meta.id, data); + if (before) { + await transactionalEntityManager.update(MiMeta, before.id, data); const metas = await transactionalEntityManager.find(MiMeta, { order: { @@ -119,6 +123,21 @@ export class MetaService implements OnApplicationShutdown { } }); + if (data.hiddenTags) { + process.nextTick(() => { + const hiddenTags = new Set(data.hiddenTags); + if (before) { + for (const previousHiddenTag of before.hiddenTags) { + hiddenTags.delete(previousHiddenTag); + } + } + + for (const hiddenTag of hiddenTags) { + this.featuredService.removeHashtagsFromRanking(hiddenTag); + } + }); + } + this.globalEventService.publishInternalEvent('metaUpdated', updated); return updated; diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 187660b295..4a41c6786d 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -299,7 +299,7 @@ export class NoteCreateService implements OnApplicationShutdown { } // Check blocking - if (data.renote && this.isQuote(data)) { + if (data.renote && !this.isQuote(data)) { if (data.renote.userHost === null) { if (data.renote.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id); @@ -755,8 +755,9 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private isQuote(note: Option): boolean { - return !!note.text || !!note.cw || !!note.files || !!note.poll; + private isQuote(note: Option): note is Option & { renote: MiNote } { + // sync with misc/is-quote.ts + return !!note.renote && (!!note.text || !!note.cw || (!!note.files && !!note.files.length) || !!note.poll); } @bindThis @@ -824,7 +825,7 @@ export class NoteCreateService implements OnApplicationShutdown { private async renderNoteOrRenoteActivity(data: Option, note: MiNote) { if (data.localOnly) return null; - const content = data.renote && this.isQuote(data) + const content = data.renote && !this.isQuote(data) ? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note) : this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note); diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index a8fdfc824a..3597cb4325 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -6,7 +6,14 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import { In } from 'typeorm'; -import type { MiRole, MiRoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js'; +import { ModuleRef } from '@nestjs/core'; +import type { + MiRole, + MiRoleAssignment, + RoleAssignmentsRepository, + RolesRepository, + UsersRepository, +} from '@/models/_.js'; import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js'; import type { MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; @@ -16,12 +23,13 @@ import { CacheService } from '@/core/CacheService.js'; import type { RoleCondFormulaValue } from '@/models/Role.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; -import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { IdService } from '@/core/IdService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import type { Packed } from '@/misc/json-schema.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; -import type { OnApplicationShutdown } from '@nestjs/common'; +import { NotificationService } from '@/core/NotificationService.js'; +import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; export type RolePolicies = { gtlAvailable: boolean; @@ -80,14 +88,17 @@ export const DEFAULT_POLICIES: RolePolicies = { }; @Injectable() -export class RoleService implements OnApplicationShutdown { +export class RoleService implements OnApplicationShutdown, OnModuleInit { private rolesCache: MemorySingleCache; private roleAssignmentByUserIdCache: MemoryKVCache; + private notificationService: NotificationService; public static AlreadyAssignedError = class extends Error {}; public static NotAssignedError = class extends Error {}; constructor( + private moduleRef: ModuleRef, + @Inject(DI.redis) private redisClient: Redis.Redis, @@ -122,6 +133,10 @@ export class RoleService implements OnApplicationShutdown { this.redisForSub.on('message', this.onMessage); } + async onModuleInit() { + this.notificationService = this.moduleRef.get(NotificationService.name); + } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -430,6 +445,12 @@ export class RoleService implements OnApplicationShutdown { this.globalEventService.publishInternalEvent('userRoleAssigned', created); + if (role.isPublic) { + this.notificationService.createNotification(userId, 'roleAssigned', { + roleId: roleId, + }); + } + if (moderator) { const user = await this.usersRepository.findOneByOrFail({ id: userId }); this.moderationLogService.log(moderator, 'assignRole', { diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index d522551aff..0ce6182769 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -3,30 +3,34 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; +import { Inject, Injectable, OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; import * as Redis from 'ioredis'; +import { ModuleRef } from '@nestjs/core'; import type { UserListMembershipsRepository } from '@/models/_.js'; import type { MiUser } from '@/models/User.js'; import type { MiUserList } from '@/models/UserList.js'; import type { MiUserListMembership } from '@/models/UserListMembership.js'; import { IdService } from '@/core/IdService.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js'; import { bindThis } from '@/decorators.js'; -import { RoleService } from '@/core/RoleService.js'; import { QueueService } from '@/core/QueueService.js'; import { RedisKVCache } from '@/misc/cache.js'; -import type { GlobalEvents } from '@/core/GlobalEventService.js'; +import { RoleService } from '@/core/RoleService.js'; @Injectable() -export class UserListService implements OnApplicationShutdown { +export class UserListService implements OnApplicationShutdown, OnModuleInit { public static TooManyUsersError = class extends Error {}; public membersCache: RedisKVCache>; + private roleService: RoleService; constructor( + private moduleRef: ModuleRef, + @Inject(DI.redis) private redisClient: Redis.Redis, @@ -38,7 +42,6 @@ export class UserListService implements OnApplicationShutdown { private userEntityService: UserEntityService, private idService: IdService, - private roleService: RoleService, private globalEventService: GlobalEventService, private proxyAccountService: ProxyAccountService, private queueService: QueueService, @@ -54,6 +57,10 @@ export class UserListService implements OnApplicationShutdown { this.redisForSub.on('message', this.onMessage); } + async onModuleInit() { + this.roleService = this.moduleRef.get(RoleService.name); + } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index e559e29517..da51797579 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -15,8 +15,8 @@ import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; import { FilterUnionByProperty, notificationTypes } from '@/types.js'; +import { RoleEntityService } from './RoleEntityService.js'; import type { OnModuleInit } from '@nestjs/common'; -import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; @@ -28,8 +28,8 @@ const NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES = new Set(['note', 'mention', 're export class NotificationEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; + private roleEntityService: RoleEntityService; private userGroupInvitationEntityService: UserGroupInvitationEntityService; - private customEmojiService: CustomEmojiService; constructor( private moduleRef: ModuleRef, @@ -49,15 +49,14 @@ export class NotificationEntityService implements OnModuleInit { //private userEntityService: UserEntityService, //private noteEntityService: NoteEntityService, //private userGroupInvitationEntityService: UserGroupInvitationEntityService, - //private customEmojiService: CustomEmojiService, ) { } onModuleInit() { this.userEntityService = this.moduleRef.get('UserEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); + this.roleEntityService = this.moduleRef.get('RoleEntityService'); this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService'); - this.customEmojiService = this.moduleRef.get('CustomEmojiService'); } @bindThis @@ -88,6 +87,7 @@ export class NotificationEntityService implements OnModuleInit { detail: false, }) ) : undefined; + const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined; return await awaitAll({ id: notification.id, @@ -99,6 +99,9 @@ export class NotificationEntityService implements OnModuleInit { ...(notification.type === 'reaction' ? { reaction: notification.reaction, } : {}), + ...(notification.type === 'roleAssigned' ? { + role: role, + } : {}), // ...(notification.type === 'pollEnded' ? { // note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { // detail: true, @@ -240,6 +243,8 @@ export class NotificationEntityService implements OnModuleInit { }); } + const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined; + return await awaitAll({ id: notification.id, createdAt: new Date(notification.createdAt).toISOString(), @@ -250,6 +255,9 @@ export class NotificationEntityService implements OnModuleInit { ...(notification.type === 'reaction' ? { reaction: notification.reaction, } : {}), + ...(notification.type === 'roleAssigned' ? { + role: role, + } : {}), ...(notification.type === 'groupInvited' ? { invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId), } : {}), diff --git a/packages/backend/src/misc/is-quote.ts b/packages/backend/src/misc/is-quote.ts index 787afac822..d53d7a6be8 100644 --- a/packages/backend/src/misc/is-quote.ts +++ b/packages/backend/src/misc/is-quote.ts @@ -7,5 +7,6 @@ import type { MiNote } from '@/models/Note.js'; // eslint-disable-next-line import/no-default-export export default function(note: MiNote): boolean { + // sync with NoteCreateService.isQuote return note.renoteId != null && (note.text != null || note.hasPoll || (note.fileIds != null && note.fileIds.length > 0)); } diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index f3d59e3e88..5441b1cc46 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -40,6 +40,7 @@ import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; import { packedSigninSchema } from '@/models/json-schema/signin.js'; import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js'; +import { packedAdSchema } from '@/models/json-schema/ad.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -52,6 +53,7 @@ export const refs = { UserList: packedUserListSchema, UserGroup: packedUserGroupSchema, + Ad: packedAdSchema, Announcement: packedAnnouncementSchema, App: packedAppSchema, MessagingMessage: packedMessagingMessageSchema, diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 6d6f7cb341..4ac6627428 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -46,11 +46,6 @@ export class MiMeta { }) public maintainerEmail: string | null; - @Column('varchar', { - length: 1024, nullable: true, - }) - public emailToReceiveAbuseReport: string | null; - @Column('boolean', { default: false, }) @@ -590,11 +585,6 @@ export class MiMeta { }) public enableIdenticonGeneration: boolean; - @Column('boolean', { - default: false, - }) - public doNotSendNotificationEmailsForAbuseReport: boolean; - @Column('jsonb', { default: { }, }) @@ -613,6 +603,13 @@ export class MiMeta { }) public manifestJsonOverride: string; + @Column('varchar', { + length: 1024, + array: true, + default: '{}', + }) + public bannedEmailDomains: string[]; + @Column('varchar', { length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey", "cherrypick" }', }) @@ -653,6 +650,16 @@ export class MiMeta { }) public notesPerOneAd: number; + @Column('boolean', { + default: false, + }) + public doNotSendNotificationEmailsForAbuseReport: boolean; + + @Column('varchar', { + length: 1024, nullable: true, + }) + public emailToReceiveAbuseReport: string | null; + @Column('boolean', { default: false, }) diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts index 3f78a3bd7e..81d000bb35 100644 --- a/packages/backend/src/models/Notification.ts +++ b/packages/backend/src/models/Notification.ts @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { notificationTypes } from '@/types.js'; import { MiUser } from './User.js'; import { MiNote } from './Note.js'; -import { MiFollowRequest } from './FollowRequest.js'; import { MiUserGroupInvitation } from './UserGroupInvitation.js'; import { MiAccessToken } from './AccessToken.js'; +import { MiRole } from './Role.js'; export type MiNotification = { type: 'note'; @@ -69,6 +68,11 @@ export type MiNotification = { id: string; createdAt: string; notifierId: MiUser['id']; +} | { + type: 'roleAssigned'; + id: string; + createdAt: string; + roleId: MiRole['id']; } | { type: 'groupInvited'; id: string; diff --git a/packages/backend/src/models/json-schema/ad.ts b/packages/backend/src/models/json-schema/ad.ts new file mode 100644 index 0000000000..d139e5a72e --- /dev/null +++ b/packages/backend/src/models/json-schema/ad.ts @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const packedAdSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, + nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + expiresAt: { + type: 'string', + optional: false, + nullable: false, + format: 'date-time', + }, + startsAt: { + type: 'string', + optional: false, + nullable: false, + format: 'date-time', + }, + place: { + type: 'string', + optional: false, + nullable: false, + }, + priority: { + type: 'string', + optional: false, + nullable: false, + }, + ratio: { + type: 'number', + optional: false, + nullable: false, + }, + url: { + type: 'string', + optional: false, + nullable: false, + }, + imageUrl: { + type: 'string', + optional: false, + nullable: false, + }, + memo: { + type: 'string', + optional: false, + nullable: false, + }, + dayOfWeek: { + type: 'integer', + optional: false, + nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 3a71aae75f..b712d3448e 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -566,9 +566,7 @@ export const packedMeDetailedOnlySchema = { mention: notificationRecieveConfig, reaction: notificationRecieveConfig, pollEnded: notificationRecieveConfig, - achievementEarned: notificationRecieveConfig, receiveFollowRequest: notificationRecieveConfig, - followRequestAccepted: notificationRecieveConfig, groupInvited: notificationRecieveConfig, }, }, diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 9230f471f8..cae60b2674 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -155,8 +155,8 @@ export class QueueProcessorService implements OnApplicationShutdown { this.systemQueueWorker .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) - .on('error', (err: Error) => systemLogger.error(`error ${err}`, { e: renderError(err) })) + .on('failed', (job, err) => systemLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) + .on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) })) .on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`)); //#endregion @@ -194,8 +194,8 @@ export class QueueProcessorService implements OnApplicationShutdown { this.dbQueueWorker .on('active', (job) => dbLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) - .on('error', (err: Error) => dbLogger.error(`error ${err}`, { e: renderError(err) })) + .on('failed', (job, err) => dbLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) + .on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) })) .on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`)); //#endregion @@ -218,8 +218,8 @@ export class QueueProcessorService implements OnApplicationShutdown { this.deliverQueueWorker .on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) .on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`)) - .on('error', (err: Error) => deliverLogger.error(`error ${err}`, { e: renderError(err) })) + .on('failed', (job, err) => deliverLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`)) + .on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) })) .on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`)); //#endregion @@ -242,8 +242,8 @@ export class QueueProcessorService implements OnApplicationShutdown { this.inboxQueueWorker .on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) .on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) - .on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) })) - .on('error', (err: Error) => inboxLogger.error(`error ${err}`, { e: renderError(err) })) + .on('failed', (job, err) => inboxLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) })) + .on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) })) .on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`)); //#endregion @@ -266,8 +266,8 @@ export class QueueProcessorService implements OnApplicationShutdown { this.webhookDeliverQueueWorker .on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) .on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`)) - .on('error', (err: Error) => webhookLogger.error(`error ${err}`, { e: renderError(err) })) + .on('failed', (job, err) => webhookLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`)) + .on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) })) .on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`)); //#endregion @@ -295,8 +295,8 @@ export class QueueProcessorService implements OnApplicationShutdown { this.relationshipQueueWorker .on('active', (job) => relationshipLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => relationshipLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) - .on('error', (err: Error) => relationshipLogger.error(`error ${err}`, { e: renderError(err) })) + .on('failed', (job, err) => relationshipLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) + .on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) })) .on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`)); //#endregion @@ -318,8 +318,8 @@ export class QueueProcessorService implements OnApplicationShutdown { this.objectStorageQueueWorker .on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) - .on('error', (err: Error) => objectStorageLogger.error(`error ${err}`, { e: renderError(err) })) + .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) + .on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) })) .on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`)); //#endregion diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts index b43c047ad6..c99e86d015 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts @@ -25,6 +25,11 @@ export const meta = { id: 'cb865949-8af5-4062-a88c-ef55e8786d1d', }, }, + res: { + type: 'object', + optional: false, nullable: false, + ref: 'User', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index d890690b3e..2af8051bb6 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -17,6 +17,12 @@ export const meta = { requireCredential: true, requireModerator: true, + res: { + type: 'object', + optional: false, + nullable: false, + ref: 'Ad', + }, } as const; export const paramDef = { @@ -63,7 +69,18 @@ export default class extends Endpoint { // eslint- ad: ad, }); - return ad; + return { + id: ad.id, + expiresAt: ad.expiresAt.toISOString(), + startsAt: ad.startsAt.toISOString(), + dayOfWeek: ad.dayOfWeek, + url: ad.url, + imageUrl: ad.imageUrl, + priority: ad.priority, + ratio: ad.ratio, + place: ad.place, + memo: ad.memo, + }; }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 3bddf22eec..5e9b888f89 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -16,6 +16,17 @@ export const meta = { requireCredential: true, requireModerator: true, + res: { + type: 'array', + optional: false, + nullable: false, + items: { + type: 'object', + optional: false, + nullable: false, + ref: 'Ad', + }, + }, } as const; export const paramDef = { @@ -46,7 +57,18 @@ export default class extends Endpoint { // eslint- } const ads = await query.limit(ps.limit).getMany(); - return ads; + return ads.map(ad => ({ + id: ad.id, + expiresAt: ad.expiresAt.toISOString(), + startsAt: ad.startsAt.toISOString(), + dayOfWeek: ad.dayOfWeek, + url: ad.url, + imageUrl: ad.imageUrl, + memo: ad.memo, + place: ad.place, + priority: ad.priority, + ratio: ad.ratio, + })); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 2193dbed6d..bee8a03d70 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -31,6 +31,8 @@ export const meta = { id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975', }, }, + + ref: 'EmojiDetailed', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/adds.ts b/packages/backend/src/server/api/endpoints/admin/emoji/adds.ts index 92bcb62943..0aea93a126 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/adds.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/adds.ts @@ -32,6 +32,8 @@ export const meta = { id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975', }, }, + + ref: 'EmojiDetailed', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index f5ac5b7626..96c8806444 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -15,6 +15,16 @@ export const meta = { kind: 'read:admin', tags: ['admin'], + res: { + type: 'array', + items: { + type: 'object', + properties: { + tablename: { type: 'string' }, + indexname: { type: 'string' }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index 374c63da1b..3d25421882 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -16,6 +16,25 @@ export const meta = { requireCredential: true, requireModerator: true, + res: { + type: 'array', + optional: false, + nullable: false, + items: { + type: 'object', + optional: false, + nullable: false, + properties: { + ip: { type: 'string' }, + createdAt: { + type: 'string', + optional: false, + nullable: false, + format: 'date-time', + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 89e9740cb8..1c085ce5f4 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -149,6 +149,14 @@ export const meta = { type: 'string', }, }, + bannedEmailDomains: { + type: 'array', + optional: true, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, preservedUsernames: { type: 'array', optional: false, nullable: false, @@ -349,14 +357,6 @@ export const meta = { type: 'string', optional: false, nullable: false, }, - doNotSendNotificationEmailsForAbuseReport: { - type: 'boolean', - optional: false, nullable: false, - }, - emailToReceiveAbuseReport: { - type: 'string', - optional: false, nullable: true, - }, policies: { type: 'object', optional: false, nullable: false, @@ -469,6 +469,14 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + doNotSendNotificationEmailsForAbuseReport: { + type: 'boolean', + optional: false, nullable: false, + }, + emailToReceiveAbuseReport: { + type: 'string', + optional: false, nullable: true, + }, enableReceivePrerelease: { type: 'boolean', optional: false, nullable: false, @@ -624,8 +632,7 @@ export default class extends Endpoint { // eslint- enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances, enableServerMachineStats: instance.enableServerMachineStats, enableIdenticonGeneration: instance.enableIdenticonGeneration, - doNotSendNotificationEmailsForAbuseReport: instance.doNotSendNotificationEmailsForAbuseReport, - emailToReceiveAbuseReport: instance.emailToReceiveAbuseReport, + bannedEmailDomains: instance.bannedEmailDomains, policies: { ...DEFAULT_POLICIES, ...instance.policies }, manifestJsonOverride: instance.manifestJsonOverride, enableFanoutTimeline: instance.enableFanoutTimeline, @@ -635,6 +642,8 @@ export default class extends Endpoint { // eslint- perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax, perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax, notesPerOneAd: instance.notesPerOneAd, + doNotSendNotificationEmailsForAbuseReport: instance.doNotSendNotificationEmailsForAbuseReport, + emailToReceiveAbuseReport: instance.emailToReceiveAbuseReport, enableReceivePrerelease: instance.enableReceivePrerelease, skipVersion: instance.skipVersion, skipCherryPickVersion: instance.skipCherryPickVersion, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index de993cd678..e0d9f5c617 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -28,6 +28,20 @@ export const meta = { id: '224eff5e-2488-4b18-b3e7-f50d94421648', }, }, + + res: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + createdAt: { type: 'string', format: 'date-time' }, + user: { ref: 'UserDetailed' }, + expiresAt: { type: 'string', format: 'date-time', nullable: true }, + }, + required: ['id', 'createdAt', 'user'], + }, + }, } as const; export const paramDef = { @@ -80,7 +94,7 @@ export default class extends Endpoint { // eslint- id: assign.id, createdAt: this.idService.parse(assign.id).date.toISOString(), user: await this.userEntityService.pack(assign.user!, me, { detail: true }), - expiresAt: assign.expiresAt, + expiresAt: assign.expiresAt?.toISOString() ?? null, }))); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 36d1aad341..4a0acf6eca 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -142,9 +142,8 @@ export const paramDef = { enableChartsForFederatedInstances: { type: 'boolean' }, enableServerMachineStats: { type: 'boolean' }, enableIdenticonGeneration: { type: 'boolean' }, - doNotSendNotificationEmailsForAbuseReport: { type: 'boolean' }, - emailToReceiveAbuseReport: { type: 'string', nullable: true }, serverRules: { type: 'array', items: { type: 'string' } }, + bannedEmailDomains: { type: 'array', items: { type: 'string' } }, preservedUsernames: { type: 'array', items: { type: 'string' } }, manifestJsonOverride: { type: 'string' }, enableFanoutTimeline: { type: 'boolean' }, @@ -161,6 +160,8 @@ export const paramDef = { type: 'string', }, }, + doNotSendNotificationEmailsForAbuseReport: { type: 'boolean' }, + emailToReceiveAbuseReport: { type: 'string', nullable: true }, enableReceivePrerelease: { type: 'boolean' }, skipVersion: { type: 'boolean' }, skipCherryPickVersion: { type: 'string', nullable: true }, @@ -596,14 +597,6 @@ export default class extends Endpoint { // eslint- set.enableIdenticonGeneration = ps.enableIdenticonGeneration; } - if (ps.doNotSendNotificationEmailsForAbuseReport !== undefined) { - set.doNotSendNotificationEmailsForAbuseReport = ps.doNotSendNotificationEmailsForAbuseReport; - } - - if (ps.emailToReceiveAbuseReport !== undefined) { - set.emailToReceiveAbuseReport = ps.emailToReceiveAbuseReport; - } - if (ps.serverRules !== undefined) { set.serverRules = ps.serverRules; } @@ -644,6 +637,18 @@ export default class extends Endpoint { // eslint- set.notesPerOneAd = ps.notesPerOneAd; } + if (ps.bannedEmailDomains !== undefined) { + set.bannedEmailDomains = ps.bannedEmailDomains; + } + + if (ps.doNotSendNotificationEmailsForAbuseReport !== undefined) { + set.doNotSendNotificationEmailsForAbuseReport = ps.doNotSendNotificationEmailsForAbuseReport; + } + + if (ps.emailToReceiveAbuseReport !== undefined) { + set.emailToReceiveAbuseReport = ps.emailToReceiveAbuseReport; + } + if (ps.enableReceivePrerelease !== undefined) { set.enableReceivePrerelease = ps.enableReceivePrerelease; } diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index 8d2c238746..2e2f1a09e6 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -11,6 +11,23 @@ export const meta = { requireCredential: false, tags: ['meta'], + + res: { + type: 'object', + nullable: true, + properties: { + params: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + type: { type: 'string' }, + }, + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index fd76ad6089..e04bbe2487 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -19,6 +19,92 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, + + res: { + type: 'object', + optional: false, + nullable: false, + properties: { + topSubInstances: { + type: 'array', + optional: false, + nullable: false, + items: { + properties: { + id: { type: 'string' }, + firstRetrievedAt: { type: 'string' }, + host: { type: 'string' }, + usersCount: { type: 'number' }, + notesCount: { type: 'number' }, + followingCount: { type: 'number' }, + followersCount: { type: 'number' }, + isNotResponding: { type: 'boolean' }, + isSuspended: { type: 'boolean' }, + isBlocked: { type: 'boolean' }, + softwareName: { type: 'string' }, + softwareVersion: { type: 'string' }, + openRegistrations: { type: 'boolean' }, + name: { type: 'string' }, + description: { type: 'string' }, + maintainerName: { type: 'string' }, + maintainerEmail: { type: 'string' }, + isSilenced: { type: 'boolean' }, + iconUrl: { type: 'string' }, + faviconUrl: { type: 'string' }, + themeColor: { type: 'string' }, + infoUpdatedAt: { + type: 'string', + nullable: true, + }, + latestRequestReceivedAt: { + type: 'string', + nullable: true, + }, + }, + }, + }, + otherFollowersCount: { type: 'number' }, + topPubInstances: { + type: 'array', + optional: false, + nullable: false, + items: { + properties: { + id: { type: 'string' }, + firstRetrievedAt: { type: 'string' }, + host: { type: 'string' }, + usersCount: { type: 'number' }, + notesCount: { type: 'number' }, + followingCount: { type: 'number' }, + followersCount: { type: 'number' }, + isNotResponding: { type: 'boolean' }, + isSuspended: { type: 'boolean' }, + isBlocked: { type: 'boolean' }, + softwareName: { type: 'string' }, + softwareVersion: { type: 'string' }, + openRegistrations: { type: 'boolean' }, + name: { type: 'string' }, + description: { type: 'string' }, + maintainerName: { type: 'string' }, + maintainerEmail: { type: 'string' }, + isSilenced: { type: 'boolean' }, + iconUrl: { type: 'string' }, + faviconUrl: { type: 'string' }, + themeColor: { type: 'string' }, + infoUpdatedAt: { + type: 'string', + nullable: true, + }, + latestRequestReceivedAt: { + type: 'string', + nullable: true, + }, + }, + }, + }, + otherFollowingCount: { type: 'number' }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts index f113e5b87a..0095d6ef56 100644 --- a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts +++ b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts @@ -32,6 +32,18 @@ export const meta = { id: '693ba8ba-b486-40df-a174-72f8279b56a4', }, }, + + res: { + type: 'object', + properties: { + type: { + type: 'string', + }, + data: { + type: 'string', + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index 283fe45b7a..f7f40f0216 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -16,6 +16,18 @@ export const meta = { requireCredential: false, allowGet: true, cacheSec: 60 * 3, + + res: { + type: 'object', + properties: { + items: { + type: 'array', + items: { + type: 'object', + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts index 12b70f06c3..df194df34e 100644 --- a/packages/backend/src/server/api/endpoints/flash/create.ts +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -27,6 +27,12 @@ export const meta = { errors: { }, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index ec48e2aeca..b029c0739c 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -16,6 +16,16 @@ export const meta = { requireCredential: false, allowGet: true, cacheSec: 60 * 1, + res: { + type: 'object', + optional: false, nullable: false, + properties: { + count: { + type: 'number', + nullable: false, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 421c1e14b6..e6a6a9de71 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -32,6 +32,16 @@ export const meta = { id: '798d6847-b1ed-4f9c-b1f9-163c42655995', }, }, + + res: { + type: 'object', + nullable: false, + optional: false, + properties: { + id: { type: 'string' }, + name: { type: 'string' }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 8218b7b09a..a851be60a6 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -36,6 +36,140 @@ export const meta = { id: 'bf32b864-449b-47b8-974e-f9a5468546f1', }, }, + + res: { + type: 'object', + nullable: false, + optional: false, + properties: { + rp: { + type: 'object', + properties: { + id: { + type: 'string', + nullable: true, + }, + }, + }, + user: { + type: 'object', + properties: { + id: { + type: 'string', + }, + name: { + type: 'string', + }, + displayName: { + type: 'string', + }, + }, + }, + challenge: { + type: 'string', + }, + pubKeyCredParams: { + type: 'array', + items: { + type: 'object', + properties: { + type: { + type: 'string', + }, + alg: { + type: 'number', + }, + }, + }, + }, + timeout: { + type: 'number', + nullable: true, + }, + excludeCredentials: { + type: 'array', + nullable: true, + items: { + type: 'object', + properties: { + id: { + type: 'string', + }, + type: { + type: 'string', + }, + transports: { + type: 'array', + items: { + type: 'string', + enum: [ + 'ble', + 'cable', + 'hybrid', + 'internal', + 'nfc', + 'smart-card', + 'usb', + ], + }, + }, + }, + }, + }, + authenticatorSelection: { + type: 'object', + nullable: true, + properties: { + authenticatorAttachment: { + type: 'string', + enum: [ + 'cross-platform', + 'platform', + ], + }, + requireResidentKey: { + type: 'boolean', + }, + userVerification: { + type: 'string', + enum: [ + 'discouraged', + 'preferred', + 'required', + ], + }, + }, + }, + attestation: { + type: 'string', + nullable: true, + enum: [ + 'direct', + 'enterprise', + 'indirect', + 'none', + ], + }, + extensions: { + type: 'object', + nullable: true, + properties: { + appid: { + type: 'string', + nullable: true, + }, + credProps: { + type: 'boolean', + nullable: true, + }, + hmacCreateSecret: { + type: 'boolean', + nullable: true, + }, + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index bb3f3591b9..8180bc38c4 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -26,6 +26,19 @@ export const meta = { id: '78d6c839-20c9-4c66-b90a-fc0542168b48', }, }, + + res: { + type: 'object', + nullable: false, + optional: false, + properties: { + qr: { type: 'string' }, + url: { type: 'string' }, + secret: { type: 'string' }, + label: { type: 'string' }, + issuer: { type: 'string' }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index 7d5020dfe5..aca50639f6 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -13,6 +13,37 @@ export const meta = { requireCredential: true, secure: true, + + res: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + name: { + type: 'string', + }, + createdAt: { + type: 'string', + format: 'date-time', + }, + lastUsedAt: { + type: 'string', + format: 'date-time', + }, + permission: { + type: 'array', + uniqueItems: true, + items: { + type: 'string', + }, + }, + }, + }, + }, } as const; export const paramDef = { @@ -50,7 +81,7 @@ export default class extends Endpoint { // eslint- id: token.id, name: token.name ?? token.app?.name, createdAt: this.idService.parse(token.id).date.toISOString(), - lastUsedAt: token.lastUsedAt, + lastUsedAt: token.lastUsedAt?.toISOString(), permission: token.permission, }))); }); diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index f7a5d05c0f..5e6d620bc7 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -14,6 +14,36 @@ export const meta = { requireCredential: true, secure: true, + + res: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + name: { + type: 'string', + }, + callbackUrl: { + type: 'string', + nullable: true, + }, + permission: { + type: 'array', + uniqueItems: true, + items: { + type: 'string', + }, + }, + isAuthorized: { + type: 'boolean', + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts index 7c75198c86..88a5d368a2 100644 --- a/packages/backend/src/server/api/endpoints/i/move.ts +++ b/packages/backend/src/server/api/endpoints/i/move.ts @@ -64,6 +64,10 @@ export const meta = { id: 'b234a14e-9ebe-4581-8000-074b3c215962', }, }, + + res: { + type: 'object', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index ff56b219b8..e97688d226 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -9,6 +9,10 @@ import { RegistryApiService } from '@/core/RegistryApiService.js'; export const meta = { requireCredential: true, + + res: { + type: 'object', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index af6fe49787..9b70cb8182 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -18,6 +18,10 @@ export const meta = { id: '97a1e8e7-c0f7-47d2-957a-92e61256e01a', }, }, + + res: { + type: 'object', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index 819a63891b..893a264338 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -18,6 +18,10 @@ export const meta = { id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a', }, }, + + res: { + type: 'object', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index f2a96464cd..c8e66ee4c3 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -3,12 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryApiService } from '@/core/RegistryApiService.js'; export const meta = { requireCredential: true, + + res: { + type: 'object', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts index a5088cab76..5abc0b0549 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts @@ -3,13 +3,35 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryApiService } from '@/core/RegistryApiService.js'; export const meta = { requireCredential: true, secure: true, + + res: { + type: 'array', + items: { + type: 'object', + properties: { + scopes: { + type: 'array', + items: { + type: 'array', + items: { + type: 'string', + }, + }, + }, + domain: { + type: 'string', + nullable: true, + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 22e0447f6b..d342a2fa96 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -40,6 +40,11 @@ export const meta = { id: 'a2defefb-f220-8849-0af6-17f816099323', }, }, + + res: { + type: 'object', + ref: 'UserDetailed', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 5f7d81cabc..47e2a0a1d6 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -27,6 +27,33 @@ export const meta = { id: '87a9bb19-111e-4e37-81d3-a3e7426453b0', }, }, + + res: { + type: 'object', + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + userId: { + type: 'string', + format: 'misskey:id', + }, + name: { type: 'string' }, + on: { + type: 'array', + items: { + type: 'string', + enum: webhookEventTypes, + }, + }, + url: { type: 'string' }, + secret: { type: 'string' }, + active: { type: 'boolean' }, + latestSentAt: { type: 'string', format: 'date-time', nullable: true }, + latestStatus: { type: 'integer', nullable: true }, + }, + }, } as const; export const paramDef = { @@ -73,7 +100,17 @@ export default class extends Endpoint { // eslint- this.globalEventService.publishInternalEvent('webhookCreated', webhook); - return webhook; + return { + id: webhook.id, + userId: webhook.userId, + name: webhook.name, + on: webhook.on, + url: webhook.url, + secret: webhook.secret, + active: webhook.active, + latestSentAt: webhook.latestSentAt?.toISOString(), + latestStatus: webhook.latestStatus, + }; }); } } diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index b9f809752b..0772145447 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -5,6 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { webhookEventTypes } from '@/models/Webhook.js'; import type { WebhooksRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; @@ -14,6 +15,36 @@ export const meta = { requireCredential: true, kind: 'read:account', + + res: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + userId: { + type: 'string', + format: 'misskey:id', + }, + name: { type: 'string' }, + on: { + type: 'array', + items: { + type: 'string', + enum: webhookEventTypes, + }, + }, + url: { type: 'string' }, + secret: { type: 'string' }, + active: { type: 'boolean' }, + latestSentAt: { type: 'string', format: 'date-time', nullable: true }, + latestStatus: { type: 'integer', nullable: true }, + }, + }, + }, } as const; export const paramDef = { @@ -33,7 +64,19 @@ export default class extends Endpoint { // eslint- userId: me.id, }); - return webhooks; + return webhooks.map(webhook => ( + { + id: webhook.id, + userId: webhook.userId, + name: webhook.name, + on: webhook.on, + url: webhook.url, + secret: webhook.secret, + active: webhook.active, + latestSentAt: webhook.latestSentAt?.toISOString(), + latestStatus: webhook.latestStatus, + } + )); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index 446cccf1b2..a81212faa6 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -5,6 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { webhookEventTypes } from '@/models/Webhook.js'; import type { WebhooksRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -23,6 +24,33 @@ export const meta = { id: '50f614d9-3047-4f7e-90d8-ad6b2d5fb098', }, }, + + res: { + type: 'object', + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + userId: { + type: 'string', + format: 'misskey:id', + }, + name: { type: 'string' }, + on: { + type: 'array', + items: { + type: 'string', + enum: webhookEventTypes, + }, + }, + url: { type: 'string' }, + secret: { type: 'string' }, + active: { type: 'boolean' }, + latestSentAt: { type: 'string', format: 'date-time', nullable: true }, + latestStatus: { type: 'integer', nullable: true }, + }, + }, } as const; export const paramDef = { @@ -49,7 +77,17 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchWebhook); } - return webhook; + return { + id: webhook.id, + userId: webhook.userId, + name: webhook.name, + on: webhook.on, + url: webhook.url, + secret: webhook.secret, + active: webhook.active, + latestSentAt: webhook.latestSentAt?.toISOString(), + latestStatus: webhook.latestStatus, + }; }); } } diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts index 6a2079af34..fc952ee8e7 100644 --- a/packages/backend/src/server/api/endpoints/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -25,6 +25,25 @@ export const meta = { id: '30aaaee3-4792-48dc-ab0d-cf501a575ac5', }, }, + + res: { + type: 'array', + items: { + type: 'object', + nullable: false, + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + user: { + type: 'object', + ref: 'User', + }, + }, + required: ['id', 'user'], + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index f6e358312b..86948b8795 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -15,6 +15,53 @@ export const meta = { cacheSec: 60 * 1, tags: ['meta'], + res: { + type: 'object', + optional: false, nullable: false, + properties: { + machine: { + type: 'string', + nullable: false, + }, + cpu: { + type: 'object', + nullable: false, + properties: { + model: { + type: 'string', + nullable: false, + }, + cores: { + type: 'number', + nullable: false, + }, + }, + }, + mem: { + type: 'object', + properties: { + total: { + type: 'number', + nullable: false, + }, + }, + }, + fs: { + type: 'object', + nullable: false, + properties: { + total: { + type: 'number', + nullable: false, + }, + used: { + type: 'number', + nullable: false, + }, + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index 97696092ce..1ec8d00481 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -12,6 +12,30 @@ export const meta = { description: 'Endpoint for testing input validation.', requireCredential: false, + + res: { + type: 'object', + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + required: { + type: 'boolean', + }, + string: { + type: 'string', + }, + default: { + type: 'string', + }, + nullableDefault: { + type: 'string', + default: 'hello', + nullable: true, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/users/achievements.ts b/packages/backend/src/server/api/endpoints/users/achievements.ts index 3506780374..45a18f4022 100644 --- a/packages/backend/src/server/api/endpoints/users/achievements.ts +++ b/packages/backend/src/server/api/endpoints/users/achievements.ts @@ -10,6 +10,21 @@ import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, + + res: { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + }, + unlockedAt: { + type: 'number', + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts index f76c27db26..dfec7ed6e7 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts @@ -25,6 +25,35 @@ export const meta = { id: '7bc05c21-1d7a-41ae-88f1-66820f4dc686', }, }, + + res: { + type: 'array', + items: { + type: 'object', + nullable: false, + properties: { + id: { + type: 'string', + format: 'misskey:id', + }, + createdAt: { + type: 'string', + format: 'date-time', + }, + userId: { + type: 'string', + format: 'misskey:id', + }, + user: { + type: 'object', + ref: 'User', + }, + withReplies: { + type: 'boolean', + }, + }, + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 075d62f5c1..6e0fe4e5f9 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -14,12 +14,28 @@ * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した * receiveFollowRequest - フォローリクエストされた * followRequestAccepted - 自分の送ったフォローリクエストが承認された + * roleAssigned - ロールが付与された * groupInvited - グループに招待された * achievementEarned - 実績を獲得 * app - アプリ通知 * test - テスト通知(サーバー側) */ -export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app', 'test'] as const; +export const notificationTypes = [ + 'note', + 'follow', + 'mention', + 'reply', + 'renote', + 'quote', + 'reaction', + 'pollEnded', + 'receiveFollowRequest', + 'followRequestAccepted', + 'roleAssigned', + 'groupInvited', + 'achievementEarned', + 'app', + 'test'] as const; export const obsoleteNotificationTypes = ['pollVote'/*, 'groupInvited'*/] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/backend/test/docker-compose.yml b/packages/backend/test/docker-compose.yml index 2e97e4d179..f51f88ccde 100644 --- a/packages/backend/test/docker-compose.yml +++ b/packages/backend/test/docker-compose.yml @@ -7,7 +7,7 @@ services: - "127.0.0.1:56312:6379" dbtest: - image: postgres:13 + image: postgres:15 ports: - "127.0.0.1:54312:5432" environment: diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 94956047d4..ed125da6d3 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -10,9 +10,8 @@ process.env.NODE_ENV = 'test'; process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING = 'true'; import * as assert from 'assert'; -import { signup, api, post, react, startServer, waitFire, sleep, uploadUrl, randomString } from '../utils.js'; +import { api, post, randomString, signup, sleep, startServer, uploadUrl } from '../utils.js'; import type { INestApplicationContext } from '@nestjs/common'; -import type * as misskey from 'cherrypick-js'; function genHost() { return randomString() + '.example.com'; @@ -366,8 +365,8 @@ describe('Timelines', () => { await api('/following/create', { userId: bob.id }, alice); await sleep(1000); const [bobFile, carolFile] = await Promise.all([ - uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'), - uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'), + uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'), + uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'), ]); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { fileIds: [bobFile.id] }); @@ -666,7 +665,7 @@ describe('Timelines', () => { test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'); + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { fileIds: [file.id] }); @@ -804,7 +803,7 @@ describe('Timelines', () => { test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'); + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { fileIds: [file.id] }); @@ -999,7 +998,7 @@ describe('Timelines', () => { const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'); + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { fileIds: [file.id] }); @@ -1158,7 +1157,7 @@ describe('Timelines', () => { test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'); + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { fileIds: [file.id] }); diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index 7feae17051..b887b9dd03 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -19,6 +19,7 @@ import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { NotificationService } from '@/core/NotificationService.js'; import { sleep } from '../utils.js'; import type { TestingModule } from '@nestjs/testing'; import type { MockFunctionMetadata } from 'jest-mock'; @@ -32,6 +33,7 @@ describe('RoleService', () => { let rolesRepository: RolesRepository; let roleAssignmentsRepository: RoleAssignmentsRepository; let metaService: jest.Mocked; + let notificationService: jest.Mocked; let clock: lolex.InstalledClock; function createUser(data: Partial = {}) { @@ -71,6 +73,16 @@ describe('RoleService', () => { CacheService, IdService, GlobalEventService, + { + provide: NotificationService, + useFactory: () => ({ + createNotification: jest.fn(), + }), + }, + { + provide: NotificationService.name, + useExisting: NotificationService, + }, ], }) .useMocker((token) => { @@ -93,6 +105,9 @@ describe('RoleService', () => { roleAssignmentsRepository = app.get(DI.roleAssignmentsRepository); metaService = app.get(MetaService) as jest.Mocked; + notificationService = app.get(NotificationService) as jest.Mocked; + + await roleService.onModuleInit(); }); afterEach(async () => { @@ -273,4 +288,57 @@ describe('RoleService', () => { expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true); }); }); + + describe('assign', () => { + test('公開ロールの場合は通知される', async () => { + const user = await createUser(); + const role = await createRole({ + isPublic: true, + name: 'a', + }); + + await roleService.assign(user.id, role.id); + + clock.uninstall(); + await sleep(100); + + const assignments = await roleAssignmentsRepository.find({ + where: { + userId: user.id, + roleId: role.id, + }, + }); + expect(assignments).toHaveLength(1); + + expect(notificationService.createNotification).toHaveBeenCalled(); + expect(notificationService.createNotification.mock.lastCall![0]).toBe(user.id); + expect(notificationService.createNotification.mock.lastCall![1]).toBe('roleAssigned'); + expect(notificationService.createNotification.mock.lastCall![2]).toEqual({ + roleId: role.id, + }); + }); + + test('非公開ロールの場合は通知されない', async () => { + const user = await createUser(); + const role = await createRole({ + isPublic: false, + name: 'a', + }); + + await roleService.assign(user.id, role.id); + + clock.uninstall(); + await sleep(100); + + const assignments = await roleAssignmentsRepository.find({ + where: { + userId: user.id, + roleId: role.id, + }, + }); + expect(assignments).toHaveLength(1); + + expect(notificationService.createNotification).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/cherrypick-js/etc/cherrypick-js.api.md b/packages/cherrypick-js/etc/cherrypick-js.api.md index ebe29eb876..4ef323d70d 100644 --- a/packages/cherrypick-js/etc/cherrypick-js.api.md +++ b/packages/cherrypick-js/etc/cherrypick-js.api.md @@ -21,6 +21,11 @@ declare namespace acct { } export { acct } +// Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type Ad = components['schemas']['Ad']; + // Warning: (ae-forgotten-export) The symbol "operations" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -59,15 +64,24 @@ type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBo // @public (undocumented) type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminAccountsFindByEmailResponse = operations['admin/accounts/find-by-email']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminAdCreateResponse = operations['admin/ad/create']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json']; // @public (undocumented) type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminAdListResponse = operations['admin/ad/list']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json']; @@ -194,12 +208,18 @@ type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/rem // @public (undocumented) type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminGetIndexStatsResponse = operations['admin/get-index-stats']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json']; // @public (undocumented) type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminGetUserIpsResponse = operations['admin/get-user-ips']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json']; @@ -284,6 +304,9 @@ type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody'][' // @public (undocumented) type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminRolesUsersResponse = operations['admin/roles/users']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminSendEmailRequest = operations['admin/send-email']['requestBody']['content']['application/json']; @@ -326,8 +349,6 @@ type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['co // @public (undocumented) type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json']; -// Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts -// // @public (undocumented) type Announcement = components['schemas']['Announcement']; @@ -1041,6 +1062,9 @@ type EmptyResponse = Record | undefined; // @public (undocumented) type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json']; +// @public (undocumented) +type EndpointResponse = operations['endpoint']['responses']['200']['content']['application/json']; + // Warning: (ae-forgotten-export) The symbol "Overwrite" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "Endpoints_2" needs to be exported by the entry point index.d.ts // @@ -1096,9 +1120,12 @@ declare namespace entities { AdminAccountsCreateResponse, AdminAccountsDeleteRequest, AdminAccountsFindByEmailRequest, + AdminAccountsFindByEmailResponse, AdminAdCreateRequest, + AdminAdCreateResponse, AdminAdDeleteRequest, AdminAdListRequest, + AdminAdListResponse, AdminAdUpdateRequest, AdminAnnouncementsCreateRequest, AdminAnnouncementsCreateResponse, @@ -1141,8 +1168,10 @@ declare namespace entities { AdminFederationRefreshRemoteInstanceMetadataRequest, AdminFederationRemoveAllFollowingRequest, AdminFederationUpdateInstanceRequest, + AdminGetIndexStatsResponse, AdminGetTableStatsResponse, AdminGetUserIpsRequest, + AdminGetUserIpsResponse, AdminInviteCreateRequest, AdminInviteCreateResponse, AdminInviteListRequest, @@ -1184,6 +1213,7 @@ declare namespace entities { AdminRolesUnassignRequest, AdminRolesUpdateDefaultPoliciesRequest, AdminRolesUsersRequest, + AdminRolesUsersResponse, AnnouncementsRequest, AnnouncementsResponse, AntennasCreateRequest, @@ -1311,6 +1341,7 @@ declare namespace entities { EmailAddressAvailableRequest, EmailAddressAvailableResponse, EndpointRequest, + EndpointResponse, EndpointsResponse, FederationFollowersRequest, FederationFollowersResponse, @@ -1324,6 +1355,7 @@ declare namespace entities { FederationUsersRequest, FederationUsersResponse, FederationStatsRequest, + FederationStatsResponse, FollowingCreateRequest, FollowingCreateResponse, FollowingDeleteRequest, @@ -1353,6 +1385,7 @@ declare namespace entities { GalleryPostsUnlikeRequest, GalleryPostsUpdateRequest, GalleryPostsUpdateResponse, + GetOnlineUsersCountResponse, GetAvatarDecorationsResponse, HashtagsListRequest, HashtagsListResponse, @@ -1366,14 +1399,19 @@ declare namespace entities { IResponse, I2faDoneRequest, I2faKeyDoneRequest, + I2faKeyDoneResponse, I2faPasswordLessRequest, I2faRegisterKeyRequest, + I2faRegisterKeyResponse, I2faRegisterRequest, + I2faRegisterResponse, I2faUpdateKeyRequest, I2faRemoveKeyRequest, I2faUnregisterRequest, IAppsRequest, + IAppsResponse, IAuthorizedAppsRequest, + IAuthorizedAppsResponse, IClaimAchievementRequest, IChangePasswordRequest, IDeleteAccountRequest, @@ -1402,11 +1440,16 @@ declare namespace entities { IReadAnnouncementRequest, IRegenerateTokenRequest, IRegistryGetAllRequest, + IRegistryGetAllResponse, IRegistryGetDetailRequest, + IRegistryGetDetailResponse, IRegistryGetRequest, + IRegistryGetResponse, IRegistryKeysWithTypeRequest, + IRegistryKeysWithTypeResponse, IRegistryKeysRequest, IRegistryRemoveRequest, + IRegistryScopesWithDomainResponse, IRegistrySetRequest, IRevokeTokenRequest, ISigninHistoryRequest, @@ -1414,13 +1457,18 @@ declare namespace entities { IUnpinRequest, IUnpinResponse, IUpdateEmailRequest, + IUpdateEmailResponse, IUpdateRequest, IUpdateResponse, IUserGroupInvitesRequest, IUserGroupInvitesResponse, IMoveRequest, + IMoveResponse, IWebhooksCreateRequest, + IWebhooksCreateResponse, + IWebhooksListResponse, IWebhooksShowRequest, + IWebhooksShowResponse, IWebhooksUpdateRequest, IWebhooksDeleteRequest, InviteCreateResponse, @@ -1519,6 +1567,7 @@ declare namespace entities { PagesUnlikeRequest, PagesUpdateRequest, FlashCreateRequest, + FlashCreateResponse, FlashDeleteRequest, FlashFeaturedResponse, FlashGenTokenRequest, @@ -1539,10 +1588,12 @@ declare namespace entities { RolesShowRequest, RolesShowResponse, RolesUsersRequest, + RolesUsersResponse, RolesNotesRequest, RolesNotesResponse, RequestResetPasswordRequest, ResetPasswordRequest, + ServerInfoResponse, StatsResponse, SwShowRegistrationRequest, SwShowRegistrationResponse, @@ -1552,6 +1603,7 @@ declare namespace entities { SwRegisterResponse, SwUnregisterRequest, TestRequest, + TestResponse, UsernameAvailableRequest, UsernameAvailableResponse, UsersRequest, @@ -1601,6 +1653,7 @@ declare namespace entities { UsersListsCreateFromPublicResponse, UsersListsUpdateMembershipRequest, UsersListsGetMembershipsRequest, + UsersListsGetMembershipsResponse, UsersNotesRequest, UsersNotesResponse, UsersPagesRequest, @@ -1623,11 +1676,14 @@ declare namespace entities { UsersStatsRequest, UsersStatsResponse, UsersAchievementsRequest, + UsersAchievementsResponse, UsersUpdateMemoRequest, UsersTranslateRequest, UsersTranslateResponse, FetchRssRequest, + FetchRssResponse, FetchExternalResourcesRequest, + FetchExternalResourcesResponse, RetentionResponse, Error_2 as Error, UserLite, @@ -1639,6 +1695,7 @@ declare namespace entities { User, UserList, UserGroup, + Ad, Announcement, App, MessagingMessage, @@ -1704,6 +1761,9 @@ type FederationShowInstanceResponse = operations['federation/show-instance']['re // @public (undocumented) type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json']; +// @public (undocumented) +type FederationStatsResponse = operations['federation/stats']['responses']['200']['content']['application/json']; + // @public (undocumented) type FederationUpdateRemoteUserRequest = operations['federation/update-remote-user']['requestBody']['content']['application/json']; @@ -1716,6 +1776,9 @@ type FederationUsersResponse = operations['federation/users']['responses']['200' // @public (undocumented) type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json']; +// @public (undocumented) +type FetchExternalResourcesResponse = operations['fetch-external-resources']['responses']['200']['content']['application/json']; + // @public (undocumented) type FetchLike = (input: string, init?: { method?: string; @@ -1733,12 +1796,18 @@ type FetchLike = (input: string, init?: { // @public (undocumented) type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json']; +// @public (undocumented) +type FetchRssResponse = operations['fetch-rss']['responses']['200']['content']['application/json']; + // @public (undocumented) type Flash = components['schemas']['Flash']; // @public (undocumented) type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json']; +// @public (undocumented) +type FlashCreateResponse = operations['flash/create']['responses']['200']['content']['application/json']; + // @public (undocumented) type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json']; @@ -1880,6 +1949,9 @@ type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses' // @public (undocumented) type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json']; +// @public (undocumented) +type GetOnlineUsersCountResponse = operations['get-online-users-count']['responses']['200']['content']['application/json']; + // @public (undocumented) type Hashtag = components['schemas']['Hashtag']; @@ -1916,15 +1988,24 @@ type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['appli // @public (undocumented) type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json']; +// @public (undocumented) +type I2faKeyDoneResponse = operations['i/2fa/key-done']['responses']['200']['content']['application/json']; + // @public (undocumented) type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json']; // @public (undocumented) type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json']; +// @public (undocumented) +type I2faRegisterKeyResponse = operations['i/2fa/register-key']['responses']['200']['content']['application/json']; + // @public (undocumented) type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json']; +// @public (undocumented) +type I2faRegisterResponse = operations['i/2fa/register']['responses']['200']['content']['application/json']; + // @public (undocumented) type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json']; @@ -1937,9 +2018,15 @@ type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['conte // @public (undocumented) type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json']; +// @public (undocumented) +type IAppsResponse = operations['i/apps']['responses']['200']['content']['application/json']; + // @public (undocumented) type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json']; +// @public (undocumented) +type IAuthorizedAppsResponse = operations['i/authorized-apps']['responses']['200']['content']['application/json']; + // @public (undocumented) type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json']; @@ -1991,6 +2078,9 @@ type IImportUserListsRequest = operations['i/import-user-lists']['requestBody'][ // @public (undocumented) type IMoveRequest = operations['i/move']['requestBody']['content']['application/json']; +// @public (undocumented) +type IMoveResponse = operations['i/move']['responses']['200']['content']['application/json']; + // @public (undocumented) type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json']; @@ -2048,21 +2138,36 @@ type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody'][' // @public (undocumented) type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json']; +// @public (undocumented) +type IRegistryGetAllResponse = operations['i/registry/get-all']['responses']['200']['content']['application/json']; + // @public (undocumented) type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json']; +// @public (undocumented) +type IRegistryGetDetailResponse = operations['i/registry/get-detail']['responses']['200']['content']['application/json']; + // @public (undocumented) type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json']; +// @public (undocumented) +type IRegistryGetResponse = operations['i/registry/get']['responses']['200']['content']['application/json']; + // @public (undocumented) type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json']; // @public (undocumented) type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json']; +// @public (undocumented) +type IRegistryKeysWithTypeResponse = operations['i/registry/keys-with-type']['responses']['200']['content']['application/json']; + // @public (undocumented) type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json']; +// @public (undocumented) +type IRegistryScopesWithDomainResponse = operations['i/registry/scopes-with-domain']['responses']['200']['content']['application/json']; + // @public (undocumented) type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json']; @@ -2090,6 +2195,9 @@ type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['appl // @public (undocumented) type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json']; +// @public (undocumented) +type IUpdateEmailResponse = operations['i/update-email']['responses']['200']['content']['application/json']; + // @public (undocumented) type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json']; @@ -2105,12 +2213,21 @@ type IUserGroupInvitesResponse = operations['i/user-group-invites']['responses'] // @public (undocumented) type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json']; +// @public (undocumented) +type IWebhooksCreateResponse = operations['i/webhooks/create']['responses']['200']['content']['application/json']; + // @public (undocumented) type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json']; +// @public (undocumented) +type IWebhooksListResponse = operations['i/webhooks/list']['responses']['200']['content']['application/json']; + // @public (undocumented) type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json']; +// @public (undocumented) +type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200']['content']['application/json']; + // @public (undocumented) type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json']; @@ -2486,7 +2603,7 @@ type Notification_2 = components['schemas']['Notification']; type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json']; // @public (undocumented) -export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "achievementEarned"]; +export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned"]; // @public (undocumented) type Page = components['schemas']['Page']; @@ -2615,6 +2732,12 @@ type RolesShowResponse = operations['roles/show']['responses']['200']['content'] // @public (undocumented) type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json']; +// @public (undocumented) +type RolesUsersResponse = operations['roles/users']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ServerInfoResponse = operations['server-info']['responses']['200']['content']['application/json']; + // @public (undocumented) type ServerStats = { cpu: number; @@ -2711,6 +2834,9 @@ type SwUpdateRegistrationResponse = operations['sw/update-registration']['respon // @public (undocumented) type TestRequest = operations['test']['requestBody']['content']['application/json']; +// @public (undocumented) +type TestResponse = operations['test']['responses']['200']['content']['application/json']; + // @public (undocumented) function toString_2(acct: Acct): string; @@ -2744,6 +2870,9 @@ type UsernameAvailableResponse = operations['username/available']['responses'][' // @public (undocumented) type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json']; +// @public (undocumented) +type UsersAchievementsResponse = operations['users/achievements']['responses']['200']['content']['application/json']; + // @public (undocumented) type UsersClipsRequest = operations['users/clips']['requestBody']['content']['application/json']; @@ -2855,6 +2984,9 @@ type UsersListsFavoriteRequest = operations['users/lists/favorite']['requestBody // @public (undocumented) type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json']; +// @public (undocumented) +type UsersListsGetMembershipsResponse = operations['users/lists/get-memberships']['responses']['200']['content']['application/json']; + // @public (undocumented) type UsersListsListRequest = operations['users/lists/list']['requestBody']['content']['application/json']; diff --git a/packages/cherrypick-js/src/consts.ts b/packages/cherrypick-js/src/consts.ts index 83d313a5fe..e769bb9e6d 100644 --- a/packages/cherrypick-js/src/consts.ts +++ b/packages/cherrypick-js/src/consts.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'achievementEarned'] as const; +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 708b94dedb..a55c8e5d22 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -3,16 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent, App } from 'vue'; +import { computed, watch, version as vueVersion, App } from 'vue'; import { compareVersions } from 'compare-versions'; import widgets from '@/widgets/index.js'; import directives from '@/directives/index.js'; import components from '@/components/index.js'; -import { version, basedMisskeyVersion, ui, lang, updateLocale, locale } from '@/config.js'; +import { version, basedMisskeyVersion, lang, updateLocale, locale } from '@/config.js'; import { applyTheme } from '@/scripts/theme.js'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; -import { i18n, updateI18n } from '@/i18n.js'; -import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js'; +import { updateI18n } from '@/i18n.js'; +import { $i, refreshAccount, login } from '@/account.js'; import { defaultStore, ColdDeviceStorage } from '@/store.js'; import { fetchInstance, instance } from '@/instance.js'; import { deviceKind } from '@/scripts/device-kind.js'; @@ -66,32 +66,23 @@ export async function common(createVue: () => App) { }); let isClientUpdated = false; + let isClientMigrated = false; //#region クライアントが更新されたかチェック const lastVersion = miLocalStorage.getItem('lastVersion'); const lastBasedMisskeyVersion = miLocalStorage.getItem('lastBasedMisskeyVersion'); - if (lastVersion !== version) { - miLocalStorage.setItem('lastVersion', version); - - // テーマリビルドするため - miLocalStorage.removeItem('theme'); - - try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため - if (lastVersion != null && compareVersions(version, lastVersion) === 1) { - isClientUpdated = true; - } - } catch (err) { /* empty */ } - } - if (lastBasedMisskeyVersion !== basedMisskeyVersion) { + if (lastVersion !== version || lastBasedMisskeyVersion !== basedMisskeyVersion) { + if (lastVersion == null) miLocalStorage.setItem('lastVersion', version); + else if (compareVersions(version, lastVersion) === 0 || compareVersions(version, lastVersion) === 1) miLocalStorage.setItem('lastVersion', version); miLocalStorage.setItem('lastBasedMisskeyVersion', basedMisskeyVersion); // テーマリビルドするため miLocalStorage.removeItem('theme'); try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため - if (lastBasedMisskeyVersion != null && compareVersions(basedMisskeyVersion, lastBasedMisskeyVersion) === 1) { + if ((lastVersion != null && compareVersions(version, lastVersion) === 1) || (lastBasedMisskeyVersion != null && compareVersions(basedMisskeyVersion, lastBasedMisskeyVersion) === 1)) { isClientUpdated = true; - } + } else if (lastVersion != null && compareVersions(version, lastVersion) === -1) isClientMigrated = true; } catch (err) { /* empty */ } } //#endregion @@ -291,6 +282,7 @@ export async function common(createVue: () => App) { return { isClientUpdated, + isClientMigrated, app, }; } diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index fc02830e7f..7f261e48dc 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -3,14 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue'; +import { createApp, markRaw, defineAsyncComponent } from 'vue'; import { common } from './common.js'; -import { version, ui, lang, updateLocale } from '@/config.js'; -import { i18n, updateI18n } from '@/i18n.js'; +import { ui } from '@/config.js'; +import { i18n } from '@/i18n.js'; import { confirm, alert, post, popup, welcomeToast } from '@/os.js'; import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; -import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js'; +import { $i, updateAccount, signout } from '@/account.js'; import { defaultStore, ColdDeviceStorage } from '@/store.js'; import { makeHotkey } from '@/scripts/hotkey.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -20,12 +20,11 @@ import { mainRouter } from '@/router.js'; import { initializeSw } from '@/scripts/initialize-sw.js'; import { deckStore } from '@/ui/deck/deck-store.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; -import { SnowfallEffect } from '@/scripts/snowfall-effect.js'; import { userName } from '@/filters/user.js'; import { vibrate } from '@/scripts/vibrate.js'; export async function mainBoot() { - const { isClientUpdated } = await common(() => createApp( + const { isClientUpdated, isClientMigrated } = await common(() => createApp( new URLSearchParams(window.location.search).has('zen') || (ui === 'deck' && deckStore.state.useSimpleUiForNonRootPages && location.pathname !== '/') ? defineAsyncComponent(() => import('@/ui/zen.vue')) : !$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) : ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) : @@ -39,6 +38,8 @@ export async function mainBoot() { if (isClientUpdated && $i) { popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed'); + } else if (isClientMigrated && $i) { + popup(defineAsyncComponent(() => import('@/components/MkMigrated.vue')), {}, {}, 'closed'); } const stream = useStream(); @@ -82,6 +83,7 @@ export async function mainBoot() { if (defaultStore.state.enableSeasonalScreenEffect) { const month = new Date().getMonth() + 1; if (month === 12 || month === 1) { + const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; new SnowfallEffect().render(); } } diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts index 2f9a9943bd..caa3e1af5c 100644 --- a/packages/frontend/src/boot/sub-boot.ts +++ b/packages/frontend/src/boot/sub-boot.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue'; +import { createApp, defineAsyncComponent } from 'vue'; import { common } from './common.js'; export async function subBoot() { diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue index 03ddb776e8..4a10cdd83a 100644 --- a/packages/frontend/src/components/MkChannelPreview.vue +++ b/packages/frontend/src/components/MkChannelPreview.vue @@ -4,49 +4,70 @@ SPDX-License-Identifier: AGPL-3.0-only --> - + + diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 1a8ea7462c..7ca2b0a79e 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -69,7 +69,7 @@ SPDX-License-Identifier: AGPL-3.0-only

- +

@@ -198,7 +198,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -66,6 +66,7 @@ function noteClick(ev: MouseEvent) { margin: 0; padding: 0; font-size: 0.95em; + -webkit-tap-highlight-color: transparent; } .avatar { diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index c2fb4f1cc9..d99f527ce2 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -88,7 +88,7 @@ if (props.detail) { } function noteClick(ev: MouseEvent) { - if (!expandOnNoteClick) ev.stopPropagation(); + if (!expandOnNoteClick || window.getSelection().toString() !== '') ev.stopPropagation(); else router.push(notePage(props.note)); } @@ -139,6 +139,7 @@ function noteClick(ev: MouseEvent) { .body { flex: 1; min-width: 0; + -webkit-tap-highlight-color: transparent; } .header { diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index ba0a3a01fb..d76444665c 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
@@ -38,6 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only + {{ i18n.ts._notification.pollEnded }} {{ i18n.ts._notification.newNote }}: + {{ i18n.ts._notification.roleAssigned }} {{ i18n.ts._notification.achievementEarned }} {{ i18n.ts._notification.testNotification }} @@ -88,6 +91,9 @@ SPDX-License-Identifier: AGPL-3.0-only +
+ {{ notification.role.name }} +
{{ i18n.ts._achievements._types['_' + notification.achievement].title }} @@ -139,7 +145,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue index 17ab881c08..d69b0d77f6 100644 --- a/packages/frontend/src/components/form/suspense.vue +++ b/packages/frontend/src/components/form/suspense.vue @@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue index e19ed3f672..4ade5727ff 100644 --- a/packages/frontend/src/pages/search.note.vue +++ b/packages/frontend/src/pages/search.note.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue index 6114153222..914deb0660 100644 --- a/packages/frontend/src/pages/search.user.vue +++ b/packages/frontend/src/pages/search.user.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index aec0176c93..67a0ab6654 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -27,10 +27,9 @@ SPDX-License-Identifier: AGPL-3.0-only