Merge pull request MisskeyIO#469 from merge-upstream
This commit is contained in:
commit
c75e058acc
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -11,20 +11,37 @@
|
||||||
-
|
-
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
## 202x.x.x (unreleased)
|
||||||
|
|
||||||
## 202x.x.x (Unreleased)
|
### General
|
||||||
|
|
||||||
|
### Client
|
||||||
|
- Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
|
||||||
|
- Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
|
||||||
|
- Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
|
||||||
|
- Fix: チャートのラベルが消えている問題を修正
|
||||||
|
- Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正
|
||||||
|
|
||||||
|
### Server
|
||||||
|
- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
|
||||||
|
- Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
|
||||||
|
|
||||||
|
## 2024.2.0
|
||||||
|
|
||||||
### Note
|
### Note
|
||||||
- 外部サイトからプラグインをインストールする場合のパスが`/install-extentions`から`/install-extensions`に変わります。現時点では以前のパスも利用できますが、非推奨です。
|
- 外部サイトからプラグインをインストールする場合のパスが`/install-extentions`から`/install-extensions`に変わります。以前のパスからは自動でリダイレクトされるようになっていますが、新しいパスに変更することをお勧めします。
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- Feat: [mCaptcha](https://github.com/mCaptcha/mCaptcha)のサポートを追加
|
- Feat: [mCaptcha](https://github.com/mCaptcha/mCaptcha)のサポートを追加
|
||||||
- Fix: リストライムラインの「リノートを表示」が正しく機能しない問題を修正
|
|
||||||
- Feat: Add support for TrueMail
|
- Feat: Add support for TrueMail
|
||||||
|
- Feat: AGPLv3ライセンスに誤って違反するのを防止する機能を追加
|
||||||
|
- 管理者がrepositoryUrlを変更したり、またはソースコードを直接頒布することを選択できるようになります
|
||||||
|
- 本体のソースコードに改変を加えた際に、ライセンスに基づく適切な案内を表示します
|
||||||
|
- Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように
|
||||||
|
- Fix: リストライムラインの「リノートを表示」が正しく機能しない問題を修正
|
||||||
- Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正
|
- Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正
|
||||||
* すべてのリモートユーザーのリアクション一覧を見えないようにします
|
* すべてのリモートユーザーのリアクション一覧を見えないようにします
|
||||||
- Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように
|
- Fix: 特定のキーワード及び正規表現にマッチする文字列を含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207
|
||||||
- Fix: 特定のキーワードを含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207
|
|
||||||
* デフォルトは空欄なので適用前と同等の動作になります
|
* デフォルトは空欄なので適用前と同等の動作になります
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
@ -57,6 +74,10 @@
|
||||||
- リモートのユーザーにローカルのみのカスタム絵文字をリアクションしようとした場合
|
- リモートのユーザーにローカルのみのカスタム絵文字をリアクションしようとした場合
|
||||||
- センシティブなリアクションを認めていないユーザーにセンシティブなカスタム絵文字をリアクションしようとした場合
|
- センシティブなリアクションを認めていないユーザーにセンシティブなカスタム絵文字をリアクションしようとした場合
|
||||||
- ロールが必要な絵文字をリアクションしようとした場合
|
- ロールが必要な絵文字をリアクションしようとした場合
|
||||||
|
- Enhance: ページ遷移時にPlayerを閉じるように
|
||||||
|
- Enhance: 通報ページのユーザをクリックした際にユーザをウィンドウで開くように
|
||||||
|
- Enhance: ノートの通報時にリモートのノートであっても自インスタンスにおけるノートのリンクを含むように
|
||||||
|
- Enhance: オフライン表示のデザインを改善・多言語対応
|
||||||
- Fix: ネイティブモードの絵文字がモノクロにならないように
|
- Fix: ネイティブモードの絵文字がモノクロにならないように
|
||||||
- Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正
|
- Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正
|
||||||
- Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正
|
- Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正
|
||||||
|
@ -67,7 +88,6 @@
|
||||||
- Fix: デッキのプロファイル作成時に名前を空にできる問題を修正
|
- Fix: デッキのプロファイル作成時に名前を空にできる問題を修正
|
||||||
- Fix: テーマ作成時に名称が空欄でも作成できてしまう問題を修正
|
- Fix: テーマ作成時に名称が空欄でも作成できてしまう問題を修正
|
||||||
- Fix: プラグインで`Plugin:register_note_post_interruptor`を使用すると、ノートが投稿できなくなる問題を修正
|
- Fix: プラグインで`Plugin:register_note_post_interruptor`を使用すると、ノートが投稿できなくなる問題を修正
|
||||||
- Enhance: ページ遷移時にPlayerを閉じるように
|
|
||||||
- Fix: iOSで大きな画像を変換してアップロードできない問題を修正
|
- Fix: iOSで大きな画像を変換してアップロードできない問題を修正
|
||||||
- Fix: 「アニメーション画像を再生しない」もしくは「データセーバー(アイコン)」を有効にしていても、アイコンデコレーションのアニメーションが停止されない問題を修正
|
- Fix: 「アニメーション画像を再生しない」もしくは「データセーバー(アイコン)」を有効にしていても、アイコンデコレーションのアニメーションが停止されない問題を修正
|
||||||
- Fix: 画像をクロップするとクロップ後の解像度が異様に低くなる問題の修正
|
- Fix: 画像をクロップするとクロップ後の解像度が異様に低くなる問題の修正
|
||||||
|
@ -77,13 +97,15 @@
|
||||||
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
||||||
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
|
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
|
||||||
- Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
|
- Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
|
||||||
|
- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
|
- Enhance: 連合先のレートリミットを超過した際にリトライするようになりました
|
||||||
- Enhance: ActivityPub Deliver queueでBodyを事前処理するように (#12916)
|
- Enhance: ActivityPub Deliver queueでBodyを事前処理するように (#12916)
|
||||||
- Enhance: クリップをエクスポートできるように
|
- Enhance: クリップをエクスポートできるように
|
||||||
- Enhance: `/files`のファイルに対してHTTP Rangeリクエストを行えるように
|
- Enhance: `/files`のファイルに対してHTTP Rangeリクエストを行えるように
|
||||||
- Enhance: `api.json`のOpenAPI Specificationを3.1.0に更新
|
- Enhance: `api.json`のOpenAPI Specificationを3.1.0に更新
|
||||||
|
- Enhance: 連合向けのノート配信を軽量化 #13192
|
||||||
- Fix: `drive/files/update`でファイル名のバリデーションが機能していない問題を修正
|
- Fix: `drive/files/update`でファイル名のバリデーションが機能していない問題を修正
|
||||||
- Fix: `notes/create`で、`text`が空白文字のみで構成されているか`null`であって、かつ`text`だけであるリクエストに対するレスポンスが400になるように変更
|
- Fix: `notes/create`で、`text`が空白文字のみで構成されているか`null`であって、かつ`text`だけであるリクエストに対するレスポンスが400になるように変更
|
||||||
- Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更
|
- Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更
|
||||||
|
@ -91,12 +113,8 @@
|
||||||
- Fix: properly handle cc followers
|
- Fix: properly handle cc followers
|
||||||
- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec
|
- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec
|
||||||
- Fix: コントロールパネル->モデレーション->「誰でも新規登録できるようにする」の初期値をONからOFFに変更 #13122
|
- Fix: コントロールパネル->モデレーション->「誰でも新規登録できるようにする」の初期値をONからOFFに変更 #13122
|
||||||
- Enhance: 連合向けのノート配信を軽量化 #13192
|
|
||||||
- Fix: リモートユーザーが復活してもキャッシュにより該当ユーザーのActivityが受け入れられないのを修正 #13273
|
- Fix: リモートユーザーが復活してもキャッシュにより該当ユーザーのActivityが受け入れられないのを修正 #13273
|
||||||
|
|
||||||
### Service Worker
|
|
||||||
- Enhance: オフライン表示のデザインを改善・多言語対応
|
|
||||||
|
|
||||||
## 2023.12.2
|
## 2023.12.2
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
|
@ -1011,6 +1011,7 @@ expired: "منتهية صلاحيته"
|
||||||
icon: "الصورة الرمزية"
|
icon: "الصورة الرمزية"
|
||||||
replies: "رد"
|
replies: "رد"
|
||||||
renotes: "أعد النشر"
|
renotes: "أعد النشر"
|
||||||
|
sourceCode: "الشفرة المصدرية"
|
||||||
flip: "اقلب"
|
flip: "اقلب"
|
||||||
lastNDays: "آخر {n} أيام"
|
lastNDays: "آخر {n} أيام"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
|
|
|
@ -855,6 +855,7 @@ youFollowing: "অনুসরণ করা হচ্ছে"
|
||||||
icon: "প্রোফাইল ছবি"
|
icon: "প্রোফাইল ছবি"
|
||||||
replies: "জবাব"
|
replies: "জবাব"
|
||||||
renotes: "রিনোট"
|
renotes: "রিনোট"
|
||||||
|
sourceCode: "সোর্স কোড"
|
||||||
flip: "উল্টান"
|
flip: "উল্টান"
|
||||||
_role:
|
_role:
|
||||||
priority: "অগ্রাধিকার"
|
priority: "অগ্রাধিকার"
|
||||||
|
|
|
@ -1167,6 +1167,7 @@ hideRepliesToOthersInTimelineAll: "Ocultar les teves respostes a tots els usuari
|
||||||
confirmShowRepliesAll: "Aquesta opció no té marxa enrere. Vols mostrar les teves respostes a tots els que segueixes a la teva línia de temps?"
|
confirmShowRepliesAll: "Aquesta opció no té marxa enrere. Vols mostrar les teves respostes a tots els que segueixes a la teva línia de temps?"
|
||||||
confirmHideRepliesAll: "Aquesta opció no té marxa enrere. Vols ocultar les teves respostes a tots els usuaris que segueixes a la línia de temps?"
|
confirmHideRepliesAll: "Aquesta opció no té marxa enrere. Vols ocultar les teves respostes a tots els usuaris que segueixes a la línia de temps?"
|
||||||
externalServices: "Serveis externs"
|
externalServices: "Serveis externs"
|
||||||
|
sourceCode: "Codi font"
|
||||||
impressum: "Impressum"
|
impressum: "Impressum"
|
||||||
impressumUrl: "Adreça URL impressum"
|
impressumUrl: "Adreça URL impressum"
|
||||||
impressumDescription: "A països, com Alemanya, la inclusió de la informació de contacte de l'operador (un Impressum) és requereix de manera legal per llocs comercials."
|
impressumDescription: "A països, com Alemanya, la inclusió de la informació de contacte de l'operador (un Impressum) és requereix de manera legal per llocs comercials."
|
||||||
|
|
|
@ -1095,6 +1095,7 @@ iHaveReadXCarefullyAndAgree: "Přečetl jsem si text \"{x}\" a souhlasím s ním
|
||||||
icon: "Avatar"
|
icon: "Avatar"
|
||||||
replies: "Odpovědět"
|
replies: "Odpovědět"
|
||||||
renotes: "Přeposlat"
|
renotes: "Přeposlat"
|
||||||
|
sourceCode: "Zdrojový kód"
|
||||||
flip: "Otočit"
|
flip: "Otočit"
|
||||||
lastNDays: "Posledních {n} dnů"
|
lastNDays: "Posledních {n} dnů"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
|
|
|
@ -1158,6 +1158,7 @@ hideRepliesToOthersInTimelineAll: "Antworten von allen momentan gefolgten Benutz
|
||||||
confirmShowRepliesAll: "Dies ist eine unwiderrufliche Aktion. Wirklich Antworten von allen momentan gefolgten Benutzern in der Chronik anzeigen?"
|
confirmShowRepliesAll: "Dies ist eine unwiderrufliche Aktion. Wirklich Antworten von allen momentan gefolgten Benutzern in der Chronik anzeigen?"
|
||||||
confirmHideRepliesAll: "Dies ist eine unwiderrufliche Aktion. Wirklich Antworten von allen momentan gefolgten Benutzern nicht in der Chronik anzeigen?"
|
confirmHideRepliesAll: "Dies ist eine unwiderrufliche Aktion. Wirklich Antworten von allen momentan gefolgten Benutzern nicht in der Chronik anzeigen?"
|
||||||
externalServices: "Externe Dienste"
|
externalServices: "Externe Dienste"
|
||||||
|
sourceCode: "Quellcode"
|
||||||
impressum: "Impressum"
|
impressum: "Impressum"
|
||||||
impressumUrl: "Impressums-URL"
|
impressumUrl: "Impressums-URL"
|
||||||
impressumDescription: "In manchen Ländern, wie Deutschland und dessen Umgebung, ist die Angabe von Betreiberinformationen (ein Impressum) bei kommerziellem Betrieb zwingend."
|
impressumDescription: "In manchen Ländern, wie Deutschland und dessen Umgebung, ist die Angabe von Betreiberinformationen (ein Impressum) bei kommerziellem Betrieb zwingend."
|
||||||
|
|
|
@ -1176,7 +1176,7 @@ hideRepliesToOthersInTimelineAll: "Hide replies to others from everyone you foll
|
||||||
confirmShowRepliesAll: "This operation is irreversible. Would you really like to show replies to others from everyone you follow in your timeline?"
|
confirmShowRepliesAll: "This operation is irreversible. Would you really like to show replies to others from everyone you follow in your timeline?"
|
||||||
confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?"
|
confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?"
|
||||||
externalServices: "External Services"
|
externalServices: "External Services"
|
||||||
sourcecode: "Source code"
|
sourceCode: "Source code"
|
||||||
repositoryUrl: "Repository URL"
|
repositoryUrl: "Repository URL"
|
||||||
feedback: "Feedback"
|
feedback: "Feedback"
|
||||||
feedbackUrl: "Feedback URL"
|
feedbackUrl: "Feedback URL"
|
||||||
|
@ -2016,8 +2016,12 @@ _permissions:
|
||||||
"read:admin:abuse-user-reports": "View user reports"
|
"read:admin:abuse-user-reports": "View user reports"
|
||||||
"write:admin:delete-account": "Delete user account"
|
"write:admin:delete-account": "Delete user account"
|
||||||
"write:admin:delete-all-files-of-a-user": "Delete all files of a user"
|
"write:admin:delete-all-files-of-a-user": "Delete all files of a user"
|
||||||
|
"read:admin:index-stats": "View database index stats"
|
||||||
|
"read:admin:table-stats": "View database table stats"
|
||||||
|
"read:admin:user-ips": "View user IP addresses"
|
||||||
"read:admin:meta": "View instance metadata"
|
"read:admin:meta": "View instance metadata"
|
||||||
"write:admin:reset-password": "Reset user password"
|
"write:admin:reset-password": "Reset user password"
|
||||||
|
"write:admin:resolve-abuse-user-report": "Resolve user report"
|
||||||
"write:admin:send-email": "Send email"
|
"write:admin:send-email": "Send email"
|
||||||
"read:admin:server-info": "View server info"
|
"read:admin:server-info": "View server info"
|
||||||
"read:admin:show-moderation-log": "View moderation log"
|
"read:admin:show-moderation-log": "View moderation log"
|
||||||
|
@ -2038,6 +2042,26 @@ _permissions:
|
||||||
"write:admin:announcements": "Manage announcements"
|
"write:admin:announcements": "Manage announcements"
|
||||||
"read:admin:announcements": "View announcements"
|
"read:admin:announcements": "View announcements"
|
||||||
"write:admin:avatar-decorations": "Manage avatar decorations"
|
"write:admin:avatar-decorations": "Manage avatar decorations"
|
||||||
|
"read:admin:avatar-decorations": "View avatar decorations"
|
||||||
|
"write:admin:federation": "Manage federation data"
|
||||||
|
"write:admin:account": "Manage user account"
|
||||||
|
"read:admin:account": "View user account"
|
||||||
|
"write:admin:emoji": "Manage emoji"
|
||||||
|
"read:admin:emoji": "View emoji"
|
||||||
|
"write:admin:queue": "Manage job queue"
|
||||||
|
"read:admin:queue": "View job queue info"
|
||||||
|
"write:admin:promo": "Manage promotion notes"
|
||||||
|
"write:admin:drive": "Manage user drive"
|
||||||
|
"read:admin:drive": "View user drive info"
|
||||||
|
"read:admin:stream": "Use WebSocket API for Admin"
|
||||||
|
"write:admin:ad": "Manage ads"
|
||||||
|
"read:admin:ad": "View ads"
|
||||||
|
"write:invite-codes": "Create invite codes"
|
||||||
|
"read:invite-codes": "Get invite codes"
|
||||||
|
"write:clip-favorite": "Manage favorited clips"
|
||||||
|
"read:clip-favorite": "View favorited clips"
|
||||||
|
"read:federation": "Get federation data"
|
||||||
|
"write:report-abuse": "Report violation"
|
||||||
_auth:
|
_auth:
|
||||||
shareAccessTitle: "Granting application permissions"
|
shareAccessTitle: "Granting application permissions"
|
||||||
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
||||||
|
|
|
@ -1166,6 +1166,7 @@ hideRepliesToOthersInTimelineAll: "Ocultar tus respuestas a otros usuarios que s
|
||||||
confirmShowRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres mostrar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
|
confirmShowRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres mostrar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
|
||||||
confirmHideRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres ocultar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
|
confirmHideRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres ocultar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
|
||||||
externalServices: "Servicios Externos"
|
externalServices: "Servicios Externos"
|
||||||
|
sourceCode: "Código fuente"
|
||||||
impressum: "Impressum"
|
impressum: "Impressum"
|
||||||
impressumUrl: "Impressum URL"
|
impressumUrl: "Impressum URL"
|
||||||
impressumDescription: "En algunos países, como Alemania, la inclusión del operador de datos (el Impressum) es requerido legalmente para sitios web comerciales."
|
impressumDescription: "En algunos países, como Alemania, la inclusión del operador de datos (el Impressum) es requerido legalmente para sitios web comerciales."
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
_lang_: "Français"
|
_lang_: "Français"
|
||||||
headlineMisskey: "Réseau relié par des notes"
|
headlineMisskey: "Réseau relié par des notes"
|
||||||
introMisskey: "Bienvenue ! Misskey est un service de microblogage décentralisé, libre et ouvert.\nÉcrivez des « notes » et partagez ce qui se passe à l’instant présent, autour de vous avec les autres 📡\nLa fonction « réactions », vous permet également d’ajouter une réaction rapide aux notes des autres utilisateur·rice·s 👍\nExplorons un nouveau monde 🚀"
|
introMisskey: "Bienvenue ! Misskey est un service de microblogage décentralisé, libre et ouvert.\nÉcrivez des « notes » et partagez ce qui se passe à l’instant présent, autour de vous avec les autres 📡\nLa fonction « réactions », vous permet également d’ajouter une réaction rapide aux notes des autres utilisateur·rice·s 👍\nExplorons un nouveau monde 🚀"
|
||||||
poweredByMisskeyDescription: "{nom} est l'un des services propulsés par la plateforme ouverte <b>Misskey</b> (appelée \"instance Misskey\")."
|
poweredByMisskeyDescription: "{name} est l'un des services propulsés par la plateforme ouverte <b>Misskey</b> (appelée \"instance Misskey\")."
|
||||||
monthAndDay: "{day}/{month}"
|
monthAndDay: "{day}/{month}"
|
||||||
search: "Rechercher"
|
search: "Rechercher"
|
||||||
notifications: "Notifications"
|
notifications: "Notifications"
|
||||||
|
@ -1136,6 +1136,7 @@ hideRepliesToOthersInTimelineAll: "Masquer les réponses de toutes les personnes
|
||||||
confirmShowRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment afficher les réponses de toutes les personnes que vous suivez dans le fil ?"
|
confirmShowRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment afficher les réponses de toutes les personnes que vous suivez dans le fil ?"
|
||||||
confirmHideRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment masquer les réponses de toutes les personnes que vous suivez dans le fil ?"
|
confirmHideRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment masquer les réponses de toutes les personnes que vous suivez dans le fil ?"
|
||||||
externalServices: "Services externes"
|
externalServices: "Services externes"
|
||||||
|
sourceCode: "Code source"
|
||||||
impressum: "Impressum"
|
impressum: "Impressum"
|
||||||
impressumUrl: "URL de l'impressum"
|
impressumUrl: "URL de l'impressum"
|
||||||
impressumDescription: "Dans certains pays comme l'Allemagne, il est obligatoire d'afficher les informations sur l'opérateur d'un site (un impressum)."
|
impressumDescription: "Dans certains pays comme l'Allemagne, il est obligatoire d'afficher les informations sur l'opérateur d'un site (un impressum)."
|
||||||
|
@ -1175,7 +1176,7 @@ _initialAccountSetting:
|
||||||
profileSetting: "Paramètres du profil"
|
profileSetting: "Paramètres du profil"
|
||||||
privacySetting: "Paramètres de confidentialité"
|
privacySetting: "Paramètres de confidentialité"
|
||||||
initialAccountSettingCompleted: "Configuration du profil terminée avec succès !"
|
initialAccountSettingCompleted: "Configuration du profil terminée avec succès !"
|
||||||
youCanContinueTutorial: "Vous pouvez procéder au tutoriel sur l'utilisation de {nom}(Misskey) ou vous arrêter ici et commencer à l'utiliser immédiatement."
|
youCanContinueTutorial: "Vous pouvez procéder au tutoriel sur l'utilisation de {name}(Misskey) ou vous arrêter ici et commencer à l'utiliser immédiatement."
|
||||||
startTutorial: "Démarrer le tutoriel"
|
startTutorial: "Démarrer le tutoriel"
|
||||||
skipAreYouSure: "Désirez-vous ignorer la configuration du profil ?"
|
skipAreYouSure: "Désirez-vous ignorer la configuration du profil ?"
|
||||||
_initialTutorial:
|
_initialTutorial:
|
||||||
|
|
|
@ -81,7 +81,7 @@ exportRequested: "Kamu telah meminta ekspor. Ini akan memakan waktu sesaat. Sete
|
||||||
importRequested: "Kamu telah meminta impor. Ini akan memakan waktu sesaat."
|
importRequested: "Kamu telah meminta impor. Ini akan memakan waktu sesaat."
|
||||||
lists: "Daftar"
|
lists: "Daftar"
|
||||||
noLists: "Kamu tidak memiliki daftar apapun"
|
noLists: "Kamu tidak memiliki daftar apapun"
|
||||||
note: "Catat"
|
note: "Catatan"
|
||||||
notes: "Catatan"
|
notes: "Catatan"
|
||||||
following: "Ikuti"
|
following: "Ikuti"
|
||||||
followers: "Pengikut"
|
followers: "Pengikut"
|
||||||
|
@ -381,8 +381,10 @@ enableHcaptcha: "Nyalakan hCaptcha"
|
||||||
hcaptchaSiteKey: "Site Key"
|
hcaptchaSiteKey: "Site Key"
|
||||||
hcaptchaSecretKey: "Secret Key"
|
hcaptchaSecretKey: "Secret Key"
|
||||||
mcaptcha: "mCaptcha"
|
mcaptcha: "mCaptcha"
|
||||||
|
enableMcaptcha: "Nyalakan mCaptcha"
|
||||||
mcaptchaSiteKey: "Site key"
|
mcaptchaSiteKey: "Site key"
|
||||||
mcaptchaSecretKey: "Secret Key"
|
mcaptchaSecretKey: "Secret Key"
|
||||||
|
mcaptchaInstanceUrl: "URL instansi mCaptcha"
|
||||||
recaptcha: "reCAPTCHA"
|
recaptcha: "reCAPTCHA"
|
||||||
enableRecaptcha: "Nyalakan reCAPTCHA"
|
enableRecaptcha: "Nyalakan reCAPTCHA"
|
||||||
recaptchaSiteKey: "Site key"
|
recaptchaSiteKey: "Site key"
|
||||||
|
@ -630,6 +632,7 @@ medium: "Sedang"
|
||||||
small: "Kecil"
|
small: "Kecil"
|
||||||
generateAccessToken: "Buat token akses"
|
generateAccessToken: "Buat token akses"
|
||||||
permission: "Izin"
|
permission: "Izin"
|
||||||
|
adminPermission: "Wewenang Izin Admin"
|
||||||
enableAll: "Aktifkan semua"
|
enableAll: "Aktifkan semua"
|
||||||
disableAll: "Nonaktifkan semua"
|
disableAll: "Nonaktifkan semua"
|
||||||
tokenRequested: "Berikan ijin akses ke akun"
|
tokenRequested: "Berikan ijin akses ke akun"
|
||||||
|
@ -1038,6 +1041,7 @@ resetPasswordConfirm: "Yakin untuk mereset kata sandimu?"
|
||||||
sensitiveWords: "Kata sensitif"
|
sensitiveWords: "Kata sensitif"
|
||||||
sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru."
|
sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru."
|
||||||
sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
|
sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
|
||||||
|
prohibitedWords: "Kata yang dilarang"
|
||||||
prohibitedWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
|
prohibitedWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
|
||||||
hiddenTags: "Tagar tersembunyi"
|
hiddenTags: "Tagar tersembunyi"
|
||||||
hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris."
|
hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris."
|
||||||
|
@ -1057,6 +1061,8 @@ limitWidthOfReaction: "Batasi lebar maksimum reaksi dan tampilkan dalam ukuran t
|
||||||
noteIdOrUrl: "ID catatan atau URL"
|
noteIdOrUrl: "ID catatan atau URL"
|
||||||
video: "Video"
|
video: "Video"
|
||||||
videos: "Video"
|
videos: "Video"
|
||||||
|
audio: "Suara"
|
||||||
|
audioFiles: "Berkas Suara"
|
||||||
dataSaver: "Penghemat data"
|
dataSaver: "Penghemat data"
|
||||||
accountMigration: "Pemindahan akun"
|
accountMigration: "Pemindahan akun"
|
||||||
accountMoved: "Pengguna ini telah berpindah ke akun baru:"
|
accountMoved: "Pengguna ini telah berpindah ke akun baru:"
|
||||||
|
@ -1160,6 +1166,7 @@ hideRepliesToOthersInTimelineAll: "Sembuyikan balasan ke lainnya dari semua oran
|
||||||
confirmShowRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menampilkan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
|
confirmShowRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menampilkan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
|
||||||
confirmHideRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menyembunyikan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
|
confirmHideRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menyembunyikan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
|
||||||
externalServices: "Layanan eksternal"
|
externalServices: "Layanan eksternal"
|
||||||
|
sourceCode: "Sumber kode"
|
||||||
impressum: "Impressum"
|
impressum: "Impressum"
|
||||||
impressumUrl: "Tautan Impressum"
|
impressumUrl: "Tautan Impressum"
|
||||||
impressumDescription: "Pada beberapa negara seperti Jerman, inklusi dari informasi kontak operator (sebuah Impressum) diperlukan secara legal untuk situs web komersil."
|
impressumDescription: "Pada beberapa negara seperti Jerman, inklusi dari informasi kontak operator (sebuah Impressum) diperlukan secara legal untuk situs web komersil."
|
||||||
|
@ -1191,10 +1198,21 @@ addMfmFunction: "Tambahkan dekorasi"
|
||||||
enableQuickAddMfmFunction: "Tampilkan pemilih MFM tingkat lanjut"
|
enableQuickAddMfmFunction: "Tampilkan pemilih MFM tingkat lanjut"
|
||||||
bubbleGame: "Bubble Game"
|
bubbleGame: "Bubble Game"
|
||||||
sfx: "Efek Suara"
|
sfx: "Efek Suara"
|
||||||
|
soundWillBePlayed: "Suara yang akan dimainkan"
|
||||||
|
showReplay: "Lihat tayangan ulang"
|
||||||
|
replay: "Tayangan ulang"
|
||||||
|
replaying: "Menayangkan Ulang"
|
||||||
|
ranking: "Peringkat"
|
||||||
lastNDays: "{n} hari terakhir"
|
lastNDays: "{n} hari terakhir"
|
||||||
backToTitle: "Ke Judul"
|
backToTitle: "Ke Judul"
|
||||||
|
hemisphere: "Letak kamu tinggal"
|
||||||
|
withSensitive: "Lampirkan catatan dengan berkas sensitif"
|
||||||
|
userSaysSomethingSensitive: "Postingan oleh {name} mengandung konten sensitif"
|
||||||
|
enableHorizontalSwipe: "Geser untuk mengganti tab"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "Cara bermain"
|
howToPlay: "Cara bermain"
|
||||||
|
_howToPlay:
|
||||||
|
section1: "Atur posisi dan jatuhkan obyek ke dalam kotak."
|
||||||
_announcement:
|
_announcement:
|
||||||
forExistingUsers: "Hanya pengguna yang telah ada"
|
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."
|
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."
|
||||||
|
@ -1256,6 +1274,8 @@ _initialTutorial:
|
||||||
note: "Baru aja makan donat berlapis coklat 🍩😋"
|
note: "Baru aja makan donat berlapis coklat 🍩😋"
|
||||||
_howToMakeAttachmentsSensitive:
|
_howToMakeAttachmentsSensitive:
|
||||||
title: "Bagaimana menandai lampiran sebagai sensitif?"
|
title: "Bagaimana menandai lampiran sebagai sensitif?"
|
||||||
|
_done:
|
||||||
|
title: "Kamu telah menyelesaikan tutorial! 🎉"
|
||||||
_serverRules:
|
_serverRules:
|
||||||
description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan."
|
description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan."
|
||||||
_serverSettings:
|
_serverSettings:
|
||||||
|
@ -1900,6 +1920,55 @@ _permissions:
|
||||||
"write:flash": "Sunting Play"
|
"write:flash": "Sunting Play"
|
||||||
"read:flash-likes": "Lihat daftar Play yang disukai"
|
"read:flash-likes": "Lihat daftar Play yang disukai"
|
||||||
"write:flash-likes": "Sunting daftar Play yang disukai"
|
"write:flash-likes": "Sunting daftar Play yang disukai"
|
||||||
|
"read:admin:abuse-user-reports": "Lihat laporan pengguna"
|
||||||
|
"write:admin:delete-account": "Hapus akun pengguna"
|
||||||
|
"write:admin:delete-all-files-of-a-user": "Hapus semua berkas dari seorang pengguna"
|
||||||
|
"read:admin:index-stats": "Lihat statistik indeks basis data"
|
||||||
|
"read:admin:table-stats": "Lihat statistik tabel basis data"
|
||||||
|
"read:admin:user-ips": "Lihat alamat IP pengguna"
|
||||||
|
"read:admin:meta": "Lihat metadata instansi"
|
||||||
|
"write:admin:reset-password": "Atur ulang kata sandi pengguna"
|
||||||
|
"write:admin:resolve-abuse-user-report": "Selesaikan laporan pengguna"
|
||||||
|
"write:admin:send-email": "Mengirim surel"
|
||||||
|
"read:admin:server-info": "Lihat informasi peladen"
|
||||||
|
"read:admin:show-moderation-log": "Lihat log moderasi"
|
||||||
|
"read:admin:show-user": "Lihat informasi pengguna privat"
|
||||||
|
"read:admin:show-users": "Lihat informasi pengguna privat"
|
||||||
|
"write:admin:suspend-user": "Tangguhkan pengguna"
|
||||||
|
"write:admin:unset-user-avatar": "Hapus avatar pengguna"
|
||||||
|
"write:admin:unset-user-banner": "Hapus banner pengguna"
|
||||||
|
"write:admin:unsuspend-user": "Batalkan penangguhan pengguna"
|
||||||
|
"write:admin:meta": "Kelola metadata instansi"
|
||||||
|
"write:admin:user-note": "Kelola moderasi catatan"
|
||||||
|
"write:admin:roles": "Kelola peran"
|
||||||
|
"read:admin:roles": "Lihat peran"
|
||||||
|
"write:admin:relays": "Kelola relay"
|
||||||
|
"read:admin:relays": "Lihat relay"
|
||||||
|
"write:admin:invite-codes": "Kelola kode undangan"
|
||||||
|
"read:admin:invite-codes": "Lihat kode undangan"
|
||||||
|
"write:admin:announcements": "Kelola pengumuman"
|
||||||
|
"read:admin:announcements": "Lihat Pengumuman"
|
||||||
|
"write:admin:avatar-decorations": "Kelola dekorasi avatar"
|
||||||
|
"read:admin:avatar-decorations": "Lihat dekorasi avatar"
|
||||||
|
"write:admin:federation": "Kelola data federasi"
|
||||||
|
"write:admin:account": "Kelola akun pengguna"
|
||||||
|
"read:admin:account": "Lihat akun pengguna"
|
||||||
|
"write:admin:emoji": "Kelola emoji"
|
||||||
|
"read:admin:emoji": "Lihat emoji"
|
||||||
|
"write:admin:queue": "Kelola antrian kerja"
|
||||||
|
"read:admin:queue": "Lihat informasi antrian kerja"
|
||||||
|
"write:admin:promo": "Kelola catatan promosi"
|
||||||
|
"write:admin:drive": "Kelola drive pengguna"
|
||||||
|
"read:admin:drive": "Kelola informasi drive pengguna"
|
||||||
|
"read:admin:stream": "Gunakan API WebSocket untuk Admin"
|
||||||
|
"write:admin:ad": "Kelola iklan"
|
||||||
|
"read:admin:ad": "Lihat iklan"
|
||||||
|
"write:invite-codes": "Membuat kode undangan"
|
||||||
|
"read:invite-codes": "Mendapatkan kode undangan"
|
||||||
|
"write:clip-favorite": "Kelola klip yang difavoritkan"
|
||||||
|
"read:clip-favorite": "Lihat klip yang difavoritkan"
|
||||||
|
"read:federation": "Mendapatkan data federasi"
|
||||||
|
"write:report-abuse": "Melaporkan pelanggaran"
|
||||||
_auth:
|
_auth:
|
||||||
shareAccessTitle: "Mendapatkan ijin akses aplikasi"
|
shareAccessTitle: "Mendapatkan ijin akses aplikasi"
|
||||||
shareAccess: "Apakah kamu ingin mengijinkan \"{name}\" untuk mengakses akun ini?"
|
shareAccess: "Apakah kamu ingin mengijinkan \"{name}\" untuk mengakses akun ini?"
|
||||||
|
@ -1954,6 +2023,7 @@ _widgets:
|
||||||
_userList:
|
_userList:
|
||||||
chooseList: "Pilih daftar"
|
chooseList: "Pilih daftar"
|
||||||
clicker: "Pengeklik"
|
clicker: "Pengeklik"
|
||||||
|
birthdayFollowings: "Pengguna yang merayakan hari ulang tahunnya hari ini"
|
||||||
_cw:
|
_cw:
|
||||||
hide: "Sembunyikan"
|
hide: "Sembunyikan"
|
||||||
show: "Lihat konten"
|
show: "Lihat konten"
|
||||||
|
@ -2320,6 +2390,41 @@ _dataSaver:
|
||||||
_code:
|
_code:
|
||||||
title: "Penyorotan kode"
|
title: "Penyorotan kode"
|
||||||
description: "Jika notasi penyorotan kode digunakan di MFM, dll. Fungsi tersebut tidak akan dimuat apabila tidak diketuk. Penyorotan sintaks membutuhkan pengunduhan berkas definisi penyorotan untuk setiap bahasa pemrograman. Oleh sebab itu, menonaktifkan pemuatan otomatis dari berkas ini dilakukan untuk mengurangi jumlah komunikasi data."
|
description: "Jika notasi penyorotan kode digunakan di MFM, dll. Fungsi tersebut tidak akan dimuat apabila tidak diketuk. Penyorotan sintaks membutuhkan pengunduhan berkas definisi penyorotan untuk setiap bahasa pemrograman. Oleh sebab itu, menonaktifkan pemuatan otomatis dari berkas ini dilakukan untuk mengurangi jumlah komunikasi data."
|
||||||
|
_hemisphere:
|
||||||
|
N: "Bumi belahan utara"
|
||||||
|
S: "Bumi belahan selatan"
|
||||||
|
caption: "Digunakan dalam beberapa pengaturan klien untuk menentukan musim."
|
||||||
_reversi:
|
_reversi:
|
||||||
|
reversi: "Reversi"
|
||||||
|
gameSettings: "Pengaturan permainan"
|
||||||
|
chooseBoard: "Pilih papan"
|
||||||
|
blackOrWhite: "Hitam/Putih"
|
||||||
|
blackIs: "{name} bermain sebagai Hitam"
|
||||||
|
rules: "Aturan"
|
||||||
|
thisGameIsStartedSoon: "Permainan akan segera dimulai"
|
||||||
|
waitingForOther: "Menunggu langkah giliran dari lawan"
|
||||||
|
waitingForMe: "Menungguh langkah giliran dari kamu"
|
||||||
|
waitingBoth: "Bersiap"
|
||||||
|
ready: "Siap"
|
||||||
|
cancelReady: "Belum siap"
|
||||||
|
opponentTurn: "Giliran lawan"
|
||||||
|
myTurn: "Giliran kamu"
|
||||||
|
turnOf: "Giliran {name}"
|
||||||
|
pastTurnOf: "Giliran {name}"
|
||||||
|
surrender: "Menyerah"
|
||||||
|
surrendered: "Telah menyerah"
|
||||||
|
timeout: "Waktu habis"
|
||||||
|
drawn: "Seri"
|
||||||
|
won: "{name} menang"
|
||||||
|
black: "Hitam"
|
||||||
|
white: "Putih"
|
||||||
total: "Jumlah"
|
total: "Jumlah"
|
||||||
|
turnCount: "Langkah ke {count}"
|
||||||
|
myGames: "Rondeku"
|
||||||
|
allGames: "Semua ronde"
|
||||||
|
ended: "Selesai"
|
||||||
|
playing: "Sedang bermain"
|
||||||
|
isLlotheo: "Pemain dengan batu yang sedikit menang (Llotheo)"
|
||||||
|
loopedMap: "Peta melingkar"
|
||||||
|
canPutEverywhere: "Keping dapat ditaruh dimana saja"
|
||||||
|
|
||||||
|
|
22
locales/index.d.ts
vendored
22
locales/index.d.ts
vendored
|
@ -4004,6 +4004,10 @@ export interface Locale extends ILocale {
|
||||||
* Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!
|
* Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!
|
||||||
*/
|
*/
|
||||||
"pleaseDonate": ParameterizedString<"host">;
|
"pleaseDonate": ParameterizedString<"host">;
|
||||||
|
/**
|
||||||
|
* 対応するソースコードは{anchor}から利用可能です。
|
||||||
|
*/
|
||||||
|
"correspondingSourceIsAvailable": ParameterizedString<"anchor">;
|
||||||
/**
|
/**
|
||||||
* ロール
|
* ロール
|
||||||
*/
|
*/
|
||||||
|
@ -4723,11 +4727,19 @@ export interface Locale extends ILocale {
|
||||||
/**
|
/**
|
||||||
* ソースコード
|
* ソースコード
|
||||||
*/
|
*/
|
||||||
"sourcecode": string;
|
"sourceCode": string;
|
||||||
|
/**
|
||||||
|
* ソースコードはまだ提供されていません。この問題の修正について管理者に問い合わせてください。
|
||||||
|
*/
|
||||||
|
"sourceCodeIsNotYetProvided": string;
|
||||||
/**
|
/**
|
||||||
* リポジトリURL
|
* リポジトリURL
|
||||||
*/
|
*/
|
||||||
"repositoryUrl": string;
|
"repositoryUrl": string;
|
||||||
|
/**
|
||||||
|
* ソースコードが公開されているリポジトリがある場合、そのURLを記入します。Misskeyを現状のまま(ソースコードにいかなる変更も加えずに)使用している場合は https://github.com/misskey-dev/misskey と記入します。
|
||||||
|
*/
|
||||||
|
"repositoryUrlDescription": string;
|
||||||
/**
|
/**
|
||||||
* フィードバック
|
* フィードバック
|
||||||
*/
|
*/
|
||||||
|
@ -6977,6 +6989,14 @@ export interface Locale extends ILocale {
|
||||||
* ソースコード
|
* ソースコード
|
||||||
*/
|
*/
|
||||||
"source": string;
|
"source": string;
|
||||||
|
/**
|
||||||
|
* オリジナル
|
||||||
|
*/
|
||||||
|
"original": string;
|
||||||
|
/**
|
||||||
|
* {name}はオリジナルのMisskeyを改変したバージョンを使用しています。
|
||||||
|
*/
|
||||||
|
"thisIsModifiedVersion": ParameterizedString<"name">;
|
||||||
/**
|
/**
|
||||||
* Misskeyを翻訳
|
* Misskeyを翻訳
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1167,6 +1167,7 @@ hideRepliesToOthersInTimelineAll: "Nascondi le risposte dei tuoi follow nella TL
|
||||||
confirmShowRepliesAll: "Questa è una attività irreversibile. Vuoi davvero includere tutte le risposte dei following in TL?"
|
confirmShowRepliesAll: "Questa è una attività irreversibile. Vuoi davvero includere tutte le risposte dei following in TL?"
|
||||||
confirmHideRepliesAll: "Questa è una attività irreversibile. Vuoi davvero escludere tutte le risposte dei following in TL?"
|
confirmHideRepliesAll: "Questa è una attività irreversibile. Vuoi davvero escludere tutte le risposte dei following in TL?"
|
||||||
externalServices: "Servizi esterni"
|
externalServices: "Servizi esterni"
|
||||||
|
sourceCode: "Codice sorgente"
|
||||||
impressum: "Dichiarazione di proprietà"
|
impressum: "Dichiarazione di proprietà"
|
||||||
impressumUrl: "URL della dichiarazione di proprietà"
|
impressumUrl: "URL della dichiarazione di proprietà"
|
||||||
impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)."
|
impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)."
|
||||||
|
|
|
@ -997,6 +997,7 @@ neverShow: "今後表示しない"
|
||||||
remindMeLater: "また後で"
|
remindMeLater: "また後で"
|
||||||
didYouLikeMisskey: "Misskeyを気に入っていただけましたか?"
|
didYouLikeMisskey: "Misskeyを気に入っていただけましたか?"
|
||||||
pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!"
|
pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!"
|
||||||
|
correspondingSourceIsAvailable: "対応するソースコードは{anchor}から利用可能です。"
|
||||||
roles: "ロール"
|
roles: "ロール"
|
||||||
role: "ロール"
|
role: "ロール"
|
||||||
noRole: "ロールはありません"
|
noRole: "ロールはありません"
|
||||||
|
@ -1176,8 +1177,10 @@ hideRepliesToOthersInTimelineAll: "TLに現在フォロー中の人全員の返
|
||||||
confirmShowRepliesAll: "この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めるようにしますか?"
|
confirmShowRepliesAll: "この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めるようにしますか?"
|
||||||
confirmHideRepliesAll: "この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めないようにしますか?"
|
confirmHideRepliesAll: "この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めないようにしますか?"
|
||||||
externalServices: "外部サービス"
|
externalServices: "外部サービス"
|
||||||
sourcecode: "ソースコード"
|
sourceCode: "ソースコード"
|
||||||
|
sourceCodeIsNotYetProvided: "ソースコードはまだ提供されていません。この問題の修正について管理者に問い合わせてください。"
|
||||||
repositoryUrl: "リポジトリURL"
|
repositoryUrl: "リポジトリURL"
|
||||||
|
repositoryUrlDescription: "ソースコードが公開されているリポジトリがある場合、そのURLを記入します。Misskeyを現状のまま(ソースコードにいかなる変更も加えずに)使用している場合は https://github.com/misskey-dev/misskey と記入します。"
|
||||||
feedback: "フィードバック"
|
feedback: "フィードバック"
|
||||||
feedbackUrl: "フィードバックURL"
|
feedbackUrl: "フィードバックURL"
|
||||||
supportThisInstance: "{name}を支援"
|
supportThisInstance: "{name}を支援"
|
||||||
|
@ -1822,6 +1825,8 @@ _aboutMisskey:
|
||||||
contributors: "コントリビューター"
|
contributors: "コントリビューター"
|
||||||
allContributors: "全てのコントリビューター"
|
allContributors: "全てのコントリビューター"
|
||||||
source: "ソースコード"
|
source: "ソースコード"
|
||||||
|
original: "オリジナル"
|
||||||
|
thisIsModifiedVersion: "{name}はオリジナルのMisskeyを改変したバージョンを使用しています。"
|
||||||
translation: "Misskeyを翻訳"
|
translation: "Misskeyを翻訳"
|
||||||
donate: "Misskeyに寄付"
|
donate: "Misskeyに寄付"
|
||||||
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰"
|
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰"
|
||||||
|
|
|
@ -1165,6 +1165,7 @@ hideRepliesToOthersInTimelineAll: "タイムラインに今フォローしとる
|
||||||
confirmShowRepliesAll: "これは元に戻せへんから慎重に決めてや。本当にタイムラインに今フォローしとる全員の返信を入れるか?"
|
confirmShowRepliesAll: "これは元に戻せへんから慎重に決めてや。本当にタイムラインに今フォローしとる全員の返信を入れるか?"
|
||||||
confirmHideRepliesAll: "これは元に戻せへんから慎重に決めてや。本当にタイムラインに今フォローしとる全員の返信を入れへんのか?"
|
confirmHideRepliesAll: "これは元に戻せへんから慎重に決めてや。本当にタイムラインに今フォローしとる全員の返信を入れへんのか?"
|
||||||
externalServices: "他のサイトのサービス"
|
externalServices: "他のサイトのサービス"
|
||||||
|
sourceCode: "ソースコード"
|
||||||
impressum: "運営者の情報"
|
impressum: "運営者の情報"
|
||||||
impressumUrl: "運営者の情報URL"
|
impressumUrl: "運営者の情報URL"
|
||||||
impressumDescription: "ドイツとかの一部んところではな、表示が義務付けられてんねん(Impressum)。"
|
impressumDescription: "ドイツとかの一部んところではな、表示が義務付けられてんねん(Impressum)。"
|
||||||
|
|
|
@ -1174,7 +1174,7 @@ hideRepliesToOthersInTimelineAll: "타임라인에 현재 팔로우 중인 사
|
||||||
confirmShowRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오게 하시겠습니까?"
|
confirmShowRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오게 하시겠습니까?"
|
||||||
confirmHideRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오지 않게 하시겠습니까?"
|
confirmHideRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오지 않게 하시겠습니까?"
|
||||||
externalServices: "외부 서비스"
|
externalServices: "외부 서비스"
|
||||||
sourcecode: "소스코드"
|
sourceCode: "소스코드"
|
||||||
repositoryUrl: "저장소 URL"
|
repositoryUrl: "저장소 URL"
|
||||||
feedback: "피드백"
|
feedback: "피드백"
|
||||||
feedbackUrl: "피드백 URL"
|
feedbackUrl: "피드백 URL"
|
||||||
|
|
|
@ -871,6 +871,7 @@ youFollowing: "Śledzeni"
|
||||||
icon: "Awatar"
|
icon: "Awatar"
|
||||||
replies: "Odpowiedz"
|
replies: "Odpowiedz"
|
||||||
renotes: "Udostępnij"
|
renotes: "Udostępnij"
|
||||||
|
sourceCode: "Kod źródłowy"
|
||||||
flip: "Odwróć"
|
flip: "Odwróć"
|
||||||
_role:
|
_role:
|
||||||
priority: "Priorytet"
|
priority: "Priorytet"
|
||||||
|
|
|
@ -1082,6 +1082,7 @@ icon: "Аватар"
|
||||||
replies: "Ответы"
|
replies: "Ответы"
|
||||||
renotes: "Репост"
|
renotes: "Репост"
|
||||||
loadReplies: "Показать ответы"
|
loadReplies: "Показать ответы"
|
||||||
|
sourceCode: "Исходный код"
|
||||||
flip: "Переворот"
|
flip: "Переворот"
|
||||||
lastNDays: "Последние {n} сут"
|
lastNDays: "Последние {n} сут"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
|
|
|
@ -919,6 +919,7 @@ youFollowing: "Sledované"
|
||||||
icon: "Avatar"
|
icon: "Avatar"
|
||||||
replies: "Odpovedať"
|
replies: "Odpovedať"
|
||||||
renotes: "Preposlať"
|
renotes: "Preposlať"
|
||||||
|
sourceCode: "Zdrojový kód"
|
||||||
flip: "Preklopiť"
|
flip: "Preklopiť"
|
||||||
lastNDays: "Posledných {n} dní"
|
lastNDays: "Posledných {n} dní"
|
||||||
_role:
|
_role:
|
||||||
|
|
|
@ -1041,6 +1041,8 @@ resetPasswordConfirm: "รีเซ็ตรหัสผ่านของคุ
|
||||||
sensitiveWords: "คำที่มีเนื้อหาละเอียดอ่อน"
|
sensitiveWords: "คำที่มีเนื้อหาละเอียดอ่อน"
|
||||||
sensitiveWordsDescription: "การเปิดเผยโน้ตทั้งหมดที่มีคำที่กำหนดค่าไว้จะถูกตั้งค่าเป็น \"หน้าแรก\" โดยอัตโนมัติ คุณยังสามารถแสดงหลายรายการได้โดยแยกรายการโดยใช้ตัวแบ่งบรรทัดได้นะ"
|
sensitiveWordsDescription: "การเปิดเผยโน้ตทั้งหมดที่มีคำที่กำหนดค่าไว้จะถูกตั้งค่าเป็น \"หน้าแรก\" โดยอัตโนมัติ คุณยังสามารถแสดงหลายรายการได้โดยแยกรายการโดยใช้ตัวแบ่งบรรทัดได้นะ"
|
||||||
sensitiveWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
|
sensitiveWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
|
||||||
|
prohibitedWords: "คำต้องห้าม"
|
||||||
|
prohibitedWordsDescription: "จะแจ้งเตือนว่าเกิดข้อผิดพลาดเมื่อพยายามโพสต์โน้ตที่มีคำที่กำหนดไว้ สามารถตั้งได้หลายคำด้วยการขึ้นบรรทัดใหม่"
|
||||||
prohibitedWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
|
prohibitedWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
|
||||||
hiddenTags: "แฮชแท็กที่ซ่อนอยู่"
|
hiddenTags: "แฮชแท็กที่ซ่อนอยู่"
|
||||||
hiddenTagsDescription: "เลือกแท็กที่จะไม่แสดงในรายการเทรนด์ สามารถลงทะเบียนหลายแท็กได้โดยขึ้นบรรทัดใหม่"
|
hiddenTagsDescription: "เลือกแท็กที่จะไม่แสดงในรายการเทรนด์ สามารถลงทะเบียนหลายแท็กได้โดยขึ้นบรรทัดใหม่"
|
||||||
|
@ -1165,6 +1167,7 @@ hideRepliesToOthersInTimelineAll: "ซ่อนตอบกลับจากท
|
||||||
confirmShowRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการแสดงการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
|
confirmShowRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการแสดงการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
|
||||||
confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
|
confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
|
||||||
externalServices: "บริการภายนอก"
|
externalServices: "บริการภายนอก"
|
||||||
|
sourceCode: "ซอร์สโค้ด"
|
||||||
impressum: "อิมเพรสชั่น"
|
impressum: "อิมเพรสชั่น"
|
||||||
impressumUrl: "URL อิมเพรสชั่น"
|
impressumUrl: "URL อิมเพรสชั่น"
|
||||||
impressumDescription: "การติดป้ายกำกับ (Impressum) มีผลบังคับใช้ในบางประเทศและภูมิภาค เช่น ประเทศเยอรมนี"
|
impressumDescription: "การติดป้ายกำกับ (Impressum) มีผลบังคับใช้ในบางประเทศและภูมิภาค เช่น ประเทศเยอรมนี"
|
||||||
|
@ -1203,6 +1206,9 @@ replaying: "กำลังรีเพลย์"
|
||||||
ranking: "อันดับ"
|
ranking: "อันดับ"
|
||||||
lastNDays: "ล่าสุด {n} วันที่แล้ว"
|
lastNDays: "ล่าสุด {n} วันที่แล้ว"
|
||||||
backToTitle: "กลับไปหน้าไตเติ้ล"
|
backToTitle: "กลับไปหน้าไตเติ้ล"
|
||||||
|
hemisphere: "พื้นที่ที่อาศัยอยู่"
|
||||||
|
withSensitive: "แสดงโน้ตที่มีไฟล์ที่ระบุว่ามีเนื้อหาละเอียดอ่อน"
|
||||||
|
userSaysSomethingSensitive: "โพสต์ที่มีไฟล์เนื้อหาละเอียดอ่อนของ {name}"
|
||||||
enableHorizontalSwipe: "ปัดเพื่อสลับแท็บ"
|
enableHorizontalSwipe: "ปัดเพื่อสลับแท็บ"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "วิธีเล่น"
|
howToPlay: "วิธีเล่น"
|
||||||
|
@ -2439,6 +2445,53 @@ _dataSaver:
|
||||||
_code:
|
_code:
|
||||||
title: "ไฮไลต์โค้ด"
|
title: "ไฮไลต์โค้ด"
|
||||||
description: "หากใช้สัญลักษณ์ไฮไลต์โค้ดใน MFM ฯลฯ สัญลักษณ์เหล่านั้นจะไม่โหลดจนกว่าจะแตะ การไฮไลต์ไวยากรณ์(syntax)จำเป็นต้องดาวน์โหลดไฟล์คำจำกัดความของไฮไลต์สำหรับแต่ละภาษา ดังนั้นการปิดใช้งานการโหลดไฟล์เหล่านี้โดยอัตโนมัติจึงคาดว่าจะช่วยลดปริมาณข้อมูลการสื่อสารได้"
|
description: "หากใช้สัญลักษณ์ไฮไลต์โค้ดใน MFM ฯลฯ สัญลักษณ์เหล่านั้นจะไม่โหลดจนกว่าจะแตะ การไฮไลต์ไวยากรณ์(syntax)จำเป็นต้องดาวน์โหลดไฟล์คำจำกัดความของไฮไลต์สำหรับแต่ละภาษา ดังนั้นการปิดใช้งานการโหลดไฟล์เหล่านี้โดยอัตโนมัติจึงคาดว่าจะช่วยลดปริมาณข้อมูลการสื่อสารได้"
|
||||||
|
_hemisphere:
|
||||||
|
N: "ซีกโลกเหนือ"
|
||||||
|
S: "ซีกโลกใต้"
|
||||||
|
caption: "ใช้เพื่อกำหนดฤดูกาลของไคลเอ็นต์"
|
||||||
_reversi:
|
_reversi:
|
||||||
|
reversi: "รีเวอร์ซี"
|
||||||
|
gameSettings: "ตั้งค่าการเล่น"
|
||||||
|
chooseBoard: "เลือกกระดาน"
|
||||||
|
blackOrWhite: "ดำ/ขาว"
|
||||||
|
blackIs: "{name}เป็นสีดำ"
|
||||||
|
rules: "กฎ"
|
||||||
|
thisGameIsStartedSoon: "การเล่นจะเริ่มแล้ว"
|
||||||
|
waitingForOther: "กำลังรออีกฝ่ายเตรียมตัวให้เสร็จ"
|
||||||
|
waitingForMe: "กำลังรอฝ่ายคุณเตรียมตัวให้เสร็จ"
|
||||||
|
waitingBoth: "กรุณาเตรียมตัว"
|
||||||
|
ready: "เตรียมตัวพร้อมแล้ว"
|
||||||
|
cancelReady: "ยกเลิกการเตรียมตัวพร้อม"
|
||||||
|
opponentTurn: "ตาอีกฝ่าย"
|
||||||
|
myTurn: "ตาของคุณ"
|
||||||
|
turnOf: "ตาของ{name}"
|
||||||
|
pastTurnOf: "ตาของ{name}"
|
||||||
|
surrender: "ยอมแพ้"
|
||||||
|
surrendered: "ยอมแพ้แล้ว"
|
||||||
|
timeout: "หมดเวลาแล้ว"
|
||||||
|
drawn: "เสมอ"
|
||||||
|
won: "{name}ชนะ"
|
||||||
|
black: "ดำ"
|
||||||
|
white: "ขาว"
|
||||||
total: "รวมทั้งหมด"
|
total: "รวมทั้งหมด"
|
||||||
|
turnCount: "ตาที่{count}"
|
||||||
|
myGames: "การเล่นของตัวเอง"
|
||||||
|
allGames: "การเล่นของทุกคน"
|
||||||
|
ended: "จบ"
|
||||||
|
playing: "กำลังเล่น"
|
||||||
|
isLlotheo: "คนที่มีตัวหมากน้อยกว่าชนะ (Roseo)"
|
||||||
|
loopedMap: "ลูปแมป"
|
||||||
|
canPutEverywhere: "โหมดที่สามารถวางได้ทุกที่"
|
||||||
|
timeLimitForEachTurn: "จำกัดเวลาต่อแต่ละตา"
|
||||||
|
freeMatch: "ฟรีแมตช์"
|
||||||
|
lookingForPlayer: "กำลังมองหาคู่ต่อสู้อยู่"
|
||||||
|
gameCanceled: "ยกเลิกการเล่นแล้ว"
|
||||||
|
shareToTlTheGameWhenStart: "โพสต์ลงไทม์ไลน์เมื่อเริ่มการเล่น"
|
||||||
|
iStartedAGame: "เริ่มเล่นหมากรีเวอร์ซีแล้ว! #MisskeyReversi"
|
||||||
|
opponentHasSettingsChanged: "อีกฝ่ายเปลี่ยนการตั้งค่า"
|
||||||
|
allowIrregularRules: "อนุญาตกฎที่ไม่ปรกติ (โหมดฟรีทุกอย่าง)"
|
||||||
|
disallowIrregularRules: "ไม่อนุญาตกฎที่ไม่ปรกติ"
|
||||||
|
_offlineScreen:
|
||||||
|
title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
|
||||||
|
header: "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
|
||||||
|
|
||||||
|
|
|
@ -911,6 +911,7 @@ youFollowing: "Підписки"
|
||||||
icon: "Аватар"
|
icon: "Аватар"
|
||||||
replies: "Відповісти"
|
replies: "Відповісти"
|
||||||
renotes: "Поширити"
|
renotes: "Поширити"
|
||||||
|
sourceCode: "Вихідний код"
|
||||||
flip: "Перевернути"
|
flip: "Перевернути"
|
||||||
lastNDays: "Останні {n} днів"
|
lastNDays: "Останні {n} днів"
|
||||||
_achievements:
|
_achievements:
|
||||||
|
|
|
@ -1045,6 +1045,7 @@ loadReplies: "Hiển thị các trả lời"
|
||||||
pinnedList: "Các mục đã được ghim"
|
pinnedList: "Các mục đã được ghim"
|
||||||
keepScreenOn: "Giữ màn hình luôn bật"
|
keepScreenOn: "Giữ màn hình luôn bật"
|
||||||
verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đường dẫn này"
|
verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đường dẫn này"
|
||||||
|
sourceCode: "Mã nguồn"
|
||||||
flip: "Lật"
|
flip: "Lật"
|
||||||
lastNDays: "{n} ngày trước"
|
lastNDays: "{n} ngày trước"
|
||||||
_announcement:
|
_announcement:
|
||||||
|
|
|
@ -11,7 +11,7 @@ password: "密码"
|
||||||
forgotPassword: "忘记密码"
|
forgotPassword: "忘记密码"
|
||||||
fetchingAsApObject: "在联邦宇宙查询中..."
|
fetchingAsApObject: "在联邦宇宙查询中..."
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
gotIt: "我明白了"
|
gotIt: "好"
|
||||||
cancel: "取消"
|
cancel: "取消"
|
||||||
noThankYou: "不用,谢谢"
|
noThankYou: "不用,谢谢"
|
||||||
enterUsername: "输入用户名"
|
enterUsername: "输入用户名"
|
||||||
|
@ -1041,6 +1041,7 @@ resetPasswordConfirm: "确定重置密码?"
|
||||||
sensitiveWords: "敏感词"
|
sensitiveWords: "敏感词"
|
||||||
sensitiveWordsDescription: "将包含设置词的帖子的可见范围设置为首页。可以通过用换行符分隔来设置多个。"
|
sensitiveWordsDescription: "将包含设置词的帖子的可见范围设置为首页。可以通过用换行符分隔来设置多个。"
|
||||||
sensitiveWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
|
sensitiveWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
|
||||||
|
prohibitedWords: "禁用词"
|
||||||
prohibitedWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
|
prohibitedWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
|
||||||
hiddenTags: "隐藏标签"
|
hiddenTags: "隐藏标签"
|
||||||
hiddenTagsDescription: "设定的标签将不会在时间线上显示。可使用换行来设置多个标签。"
|
hiddenTagsDescription: "设定的标签将不会在时间线上显示。可使用换行来设置多个标签。"
|
||||||
|
@ -1165,6 +1166,7 @@ hideRepliesToOthersInTimelineAll: "在时间线中隐藏现在关注的所有人
|
||||||
confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中包含现在关注的所有人的回复吗?"
|
confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中包含现在关注的所有人的回复吗?"
|
||||||
confirmHideRepliesAll: "此操作不可撤销。确认要在时间线中隐藏现在关注的所有人的回复吗?"
|
confirmHideRepliesAll: "此操作不可撤销。确认要在时间线中隐藏现在关注的所有人的回复吗?"
|
||||||
externalServices: "外部服务"
|
externalServices: "外部服务"
|
||||||
|
sourceCode: "源代码"
|
||||||
impressum: "运营商信息"
|
impressum: "运营商信息"
|
||||||
impressumUrl: "运营商信息地址"
|
impressumUrl: "运营商信息地址"
|
||||||
impressumDescription: "德国等国家和地区有义务展示此类信息(Impressum)。"
|
impressumDescription: "德国等国家和地区有义务展示此类信息(Impressum)。"
|
||||||
|
|
|
@ -66,7 +66,7 @@ showMore: "載入更多"
|
||||||
showLess: "關閉"
|
showLess: "關閉"
|
||||||
youGotNewFollower: "您有新的追隨者"
|
youGotNewFollower: "您有新的追隨者"
|
||||||
receiveFollowRequest: "您有新的追隨請求"
|
receiveFollowRequest: "您有新的追隨請求"
|
||||||
followRequestAccepted: "追隨請求已接受"
|
followRequestAccepted: "追隨請求已被接受"
|
||||||
mention: "提及"
|
mention: "提及"
|
||||||
mentions: "提及"
|
mentions: "提及"
|
||||||
directNotes: "私訊"
|
directNotes: "私訊"
|
||||||
|
@ -604,7 +604,7 @@ inboxUrl: "收件夾URL"
|
||||||
addedRelays: "已加入的中繼器"
|
addedRelays: "已加入的中繼器"
|
||||||
serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。"
|
serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。"
|
||||||
deletedNote: "已刪除的貼文"
|
deletedNote: "已刪除的貼文"
|
||||||
invisibleNote: "私密的貼文"
|
invisibleNote: "私人貼文"
|
||||||
enableInfiniteScroll: "啟用自動滾動頁面模式"
|
enableInfiniteScroll: "啟用自動滾動頁面模式"
|
||||||
visibility: "可見性"
|
visibility: "可見性"
|
||||||
poll: "票選活動"
|
poll: "票選活動"
|
||||||
|
@ -1167,6 +1167,7 @@ hideRepliesToOthersInTimelineAll: "在時間軸不包含追隨中所有人的回
|
||||||
confirmShowRepliesAll: "進行此操作後無法復原。您真的希望時間軸「包含」您目前追隨的所有人的回覆嗎?"
|
confirmShowRepliesAll: "進行此操作後無法復原。您真的希望時間軸「包含」您目前追隨的所有人的回覆嗎?"
|
||||||
confirmHideRepliesAll: "進行此操作後無法復原。您真的希望時間軸「不包含」您目前追隨的所有人的回覆嗎?"
|
confirmHideRepliesAll: "進行此操作後無法復原。您真的希望時間軸「不包含」您目前追隨的所有人的回覆嗎?"
|
||||||
externalServices: "外部服務"
|
externalServices: "外部服務"
|
||||||
|
sourceCode: "原始碼"
|
||||||
impressum: "營運者資訊"
|
impressum: "營運者資訊"
|
||||||
impressumUrl: "營運者資訊網址"
|
impressumUrl: "營運者資訊網址"
|
||||||
impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。"
|
impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。"
|
||||||
|
@ -1195,7 +1196,7 @@ overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
|
||||||
seasonalScreenEffect: "隨季節變換畫面的呈現"
|
seasonalScreenEffect: "隨季節變換畫面的呈現"
|
||||||
decorate: "設置頭像裝飾"
|
decorate: "設置頭像裝飾"
|
||||||
addMfmFunction: "插入MFM功能語法"
|
addMfmFunction: "插入MFM功能語法"
|
||||||
enableQuickAddMfmFunction: "顯示高級MFM選擇器"
|
enableQuickAddMfmFunction: "顯示高級 MFM 選擇器"
|
||||||
bubbleGame: "氣泡遊戲"
|
bubbleGame: "氣泡遊戲"
|
||||||
sfx: "音效"
|
sfx: "音效"
|
||||||
soundWillBePlayed: "將播放音效"
|
soundWillBePlayed: "將播放音效"
|
||||||
|
@ -2096,7 +2097,7 @@ _poll:
|
||||||
deadlineTime: "小時"
|
deadlineTime: "小時"
|
||||||
duration: "時長"
|
duration: "時長"
|
||||||
votesCount: "{n} 票"
|
votesCount: "{n} 票"
|
||||||
totalVotes: "一共{n}票"
|
totalVotes: "合計 {n} 票"
|
||||||
vote: "投票"
|
vote: "投票"
|
||||||
showResult: "顯示結果"
|
showResult: "顯示結果"
|
||||||
voted: "已投票"
|
voted: "已投票"
|
||||||
|
|
15
package.json
15
package.json
|
@ -46,28 +46,29 @@
|
||||||
"cleanall": "pnpm clean-all"
|
"cleanall": "pnpm clean-all"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "3.6.0",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"sharp": "0.33.2"
|
"sharp": "0.33.2",
|
||||||
|
"@tensorflow/tfjs-core": "4.17.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssnano": "6.0.3",
|
"cssnano": "6.0.3",
|
||||||
"execa": "8.0.1",
|
"execa": "8.0.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"postcss": "8.4.35",
|
"postcss": "8.4.35",
|
||||||
"terser": "5.27.0",
|
"terser": "5.27.2",
|
||||||
"typescript": "5.3.3"
|
"typescript": "5.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "6.21.0",
|
"@typescript-eslint/eslint-plugin": "7.0.2",
|
||||||
"@typescript-eslint/parser": "6.21.0",
|
"@typescript-eslint/parser": "7.0.2",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.6.4",
|
"cypress": "13.6.5",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"start-server-and-test": "2.0.3"
|
"start-server-and-test": "2.0.3"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tensorflow/tfjs-core": "4.4.0"
|
"@tensorflow/tfjs-core": "4.17.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MakeRepositoryUrlNullable1707808106310 {
|
||||||
|
name = 'MakeRepositoryUrlNullable1707808106310'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" DROP NOT NULL`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET NOT NULL`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class RepositoryUrlFromSyuiloToMisskeyDev1708266695091 {
|
||||||
|
name = 'RepositoryUrlFromSyuiloToMisskeyDev1708266695091'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://github.com/misskey-dev/misskey' WHERE "repositoryUrl" = 'https://github.com/syuilo/misskey'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
// no valid down migration
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,8 +46,8 @@
|
||||||
"@swc/core-win32-arm64-msvc": "1.3.107",
|
"@swc/core-win32-arm64-msvc": "1.3.107",
|
||||||
"@swc/core-win32-ia32-msvc": "1.3.107",
|
"@swc/core-win32-ia32-msvc": "1.3.107",
|
||||||
"@swc/core-win32-x64-msvc": "1.3.107",
|
"@swc/core-win32-x64-msvc": "1.3.107",
|
||||||
"@tensorflow/tfjs": "4.4.0",
|
"@tensorflow/tfjs": "4.17.0",
|
||||||
"@tensorflow/tfjs-node": "4.4.0",
|
"@tensorflow/tfjs-node": "4.17.0",
|
||||||
"bufferutil": "4.0.8",
|
"bufferutil": "4.0.8",
|
||||||
"slacc-android-arm-eabi": "0.0.10",
|
"slacc-android-arm-eabi": "0.0.10",
|
||||||
"slacc-android-arm64": "0.0.10",
|
"slacc-android-arm64": "0.0.10",
|
||||||
|
@ -65,27 +65,27 @@
|
||||||
"utf-8-validate": "6.0.3"
|
"utf-8-validate": "6.0.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.412.0",
|
"@aws-sdk/client-s3": "3.515.0",
|
||||||
"@aws-sdk/lib-storage": "3.412.0",
|
"@aws-sdk/lib-storage": "3.515.0",
|
||||||
"@bull-board/api": "5.14.0",
|
"@bull-board/api": "5.14.1",
|
||||||
"@bull-board/fastify": "5.14.0",
|
"@bull-board/fastify": "5.14.1",
|
||||||
"@bull-board/ui": "5.14.0",
|
"@bull-board/ui": "5.14.1",
|
||||||
"@discordapp/twemoji": "15.0.2",
|
"@discordapp/twemoji": "15.0.2",
|
||||||
"@fastify/accepts": "4.3.0",
|
"@fastify/accepts": "4.3.0",
|
||||||
"@fastify/cookie": "9.3.1",
|
"@fastify/cookie": "9.3.1",
|
||||||
"@fastify/cors": "8.5.0",
|
"@fastify/cors": "9.0.1",
|
||||||
"@fastify/express": "2.3.0",
|
"@fastify/express": "2.3.0",
|
||||||
"@fastify/http-proxy": "9.3.0",
|
"@fastify/http-proxy": "9.4.0",
|
||||||
"@fastify/multipart": "8.1.0",
|
"@fastify/multipart": "8.1.0",
|
||||||
"@fastify/static": "6.12.0",
|
"@fastify/static": "7.0.1",
|
||||||
"@fastify/view": "8.2.0",
|
"@fastify/view": "9.0.0",
|
||||||
"@misskey-dev/sharp-read-bmp": "^1.1.1",
|
"@misskey-dev/sharp-read-bmp": "^1.2.0",
|
||||||
"@misskey-dev/summaly": "^5.0.3",
|
"@misskey-dev/summaly": "^5.0.3",
|
||||||
"@nestjs/common": "10.3.2",
|
"@nestjs/common": "10.3.3",
|
||||||
"@nestjs/core": "10.3.2",
|
"@nestjs/core": "10.3.3",
|
||||||
"@nestjs/testing": "10.3.2",
|
"@nestjs/testing": "10.3.3",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@simplewebauthn/server": "9.0.2",
|
"@simplewebauthn/server": "9.0.3",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
"@smithy/node-http-handler": "2.3.1",
|
"@smithy/node-http-handler": "2.3.1",
|
||||||
"@swc/cli": "0.1.65",
|
"@swc/cli": "0.1.65",
|
||||||
|
@ -98,18 +98,18 @@
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "5.1.9",
|
"bullmq": "5.3.0",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.2",
|
"cbor": "9.0.2",
|
||||||
"chalk": "5.3.0",
|
"chalk": "5.3.0",
|
||||||
"chalk-template": "1.1.0",
|
"chalk-template": "1.1.0",
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "3.6.0",
|
||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"color-convert": "2.0.1",
|
"color-convert": "2.0.1",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "3.3.1",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"fastify": "4.26.0",
|
"fastify": "4.26.1",
|
||||||
"fastify-raw-body": "4.3.0",
|
"fastify-raw-body": "4.3.0",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "19.0.0",
|
"file-type": "19.0.0",
|
||||||
|
@ -135,11 +135,11 @@
|
||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"nanoid": "5.0.5",
|
"nanoid": "5.0.6",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "6.9.9",
|
"nodemailer": "6.9.9",
|
||||||
"nsfwjs": "2.4.2",
|
"nsfwjs": "3.0.0",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.0",
|
||||||
"oauth2orize": "1.12.0",
|
"oauth2orize": "1.12.0",
|
||||||
"oauth2orize-pkce": "0.1.2",
|
"oauth2orize-pkce": "0.1.2",
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
"otpauth": "9.2.2",
|
"otpauth": "9.2.2",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.3",
|
"pg": "8.11.3",
|
||||||
"pino": "8.18.0",
|
"pino": "8.19.0",
|
||||||
"pino-pretty": "10.3.1",
|
"pino-pretty": "10.3.1",
|
||||||
"pkce-challenge": "4.1.0",
|
"pkce-challenge": "4.1.0",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
|
@ -160,17 +160,17 @@
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"re2": "1.20.9",
|
"re2": "1.20.9",
|
||||||
"redis-lock": "0.1.4",
|
"redis-lock": "0.1.4",
|
||||||
"reflect-metadata": "0.1.14",
|
"reflect-metadata": "0.2.1",
|
||||||
"rename": "1.0.4",
|
"rename": "1.0.4",
|
||||||
"rss-parser": "3.13.0",
|
"rss-parser": "3.13.0",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
"sanitize-html": "2.11.0",
|
"sanitize-html": "2.12.0",
|
||||||
"secure-json-parse": "2.7.0",
|
"secure-json-parse": "2.7.0",
|
||||||
"sharp": "0.33.2",
|
"sharp": "0.33.2",
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"systeminformation": "5.21.24",
|
"systeminformation": "5.22.0",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.8",
|
||||||
|
@ -186,7 +186,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.7.0",
|
"@jest/globals": "29.7.0",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@nestjs/platform-express": "10.3.2",
|
"@nestjs/platform-express": "10.3.3",
|
||||||
"@simplewebauthn/types": "9.0.1",
|
"@simplewebauthn/types": "9.0.1",
|
||||||
"@swc/jest": "0.2.36",
|
"@swc/jest": "0.2.36",
|
||||||
"@types/accepts": "1.3.7",
|
"@types/accepts": "1.3.7",
|
||||||
|
@ -204,20 +204,20 @@
|
||||||
"@types/jsrsasign": "10.5.12",
|
"@types/jsrsasign": "10.5.12",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "20.11.17",
|
"@types/node": "20.11.19",
|
||||||
"@types/nodemailer": "6.4.14",
|
"@types/nodemailer": "6.4.14",
|
||||||
"@types/oauth": "0.9.4",
|
"@types/oauth": "0.9.4",
|
||||||
"@types/oauth2orize": "1.11.3",
|
"@types/oauth2orize": "1.11.3",
|
||||||
"@types/oauth2orize-pkce": "0.1.2",
|
"@types/oauth2orize-pkce": "0.1.2",
|
||||||
"@types/pg": "8.11.0",
|
"@types/pg": "8.11.0",
|
||||||
"@types/pug": "2.0.10",
|
"@types/pug": "2.0.10",
|
||||||
"@types/punycode": "2.1.3",
|
"@types/punycode": "2.1.4",
|
||||||
"@types/qrcode": "1.5.5",
|
"@types/qrcode": "1.5.5",
|
||||||
"@types/random-seed": "0.3.5",
|
"@types/random-seed": "0.3.5",
|
||||||
"@types/ratelimiter": "3.4.6",
|
"@types/ratelimiter": "3.4.6",
|
||||||
"@types/rename": "1.0.7",
|
"@types/rename": "1.0.7",
|
||||||
"@types/sanitize-html": "2.11.0",
|
"@types/sanitize-html": "2.11.0",
|
||||||
"@types/semver": "7.5.6",
|
"@types/semver": "7.5.7",
|
||||||
"@types/simple-oauth2": "5.0.7",
|
"@types/simple-oauth2": "5.0.7",
|
||||||
"@types/sinonjs__fake-timers": "8.1.5",
|
"@types/sinonjs__fake-timers": "8.1.5",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
|
@ -225,8 +225,8 @@
|
||||||
"@types/vary": "1.1.3",
|
"@types/vary": "1.1.3",
|
||||||
"@types/web-push": "3.6.3",
|
"@types/web-push": "3.6.3",
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "6.21.0",
|
"@typescript-eslint/eslint-plugin": "7.0.2",
|
||||||
"@typescript-eslint/parser": "6.21.0",
|
"@typescript-eslint/parser": "7.0.2",
|
||||||
"aws-sdk-client-mock": "3.0.1",
|
"aws-sdk-client-mock": "3.0.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
|
|
|
@ -16,10 +16,10 @@ import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CacheService implements OnApplicationShutdown {
|
export class CacheService implements OnApplicationShutdown {
|
||||||
public userByIdCache: MemoryKVCache<MiUser, MiUser | string>;
|
public userByIdCache: MemoryKVCache<MiUser>;
|
||||||
public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null, string | null>;
|
public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null>;
|
||||||
public localUserByIdCache: MemoryKVCache<MiLocalUser>;
|
public localUserByIdCache: MemoryKVCache<MiLocalUser>;
|
||||||
public uriPersonCache: MemoryKVCache<MiUser | null, string | null>;
|
public uriPersonCache: MemoryKVCache<MiUser | null>;
|
||||||
public userProfileCache: RedisKVCache<MiUserProfile>;
|
public userProfileCache: RedisKVCache<MiUserProfile>;
|
||||||
public userMutingsCache: RedisKVCache<Set<string>>;
|
public userMutingsCache: RedisKVCache<Set<string>>;
|
||||||
public userBlockingCache: RedisKVCache<Set<string>>;
|
public userBlockingCache: RedisKVCache<Set<string>>;
|
||||||
|
@ -56,42 +56,10 @@ export class CacheService implements OnApplicationShutdown {
|
||||||
) {
|
) {
|
||||||
//this.onMessage = this.onMessage.bind(this);
|
//this.onMessage = this.onMessage.bind(this);
|
||||||
|
|
||||||
const localUserByIdCache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 60 * 6 /* 6h */);
|
this.userByIdCache = new MemoryKVCache<MiUser>(Infinity);
|
||||||
this.localUserByIdCache = localUserByIdCache;
|
this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null>(Infinity);
|
||||||
|
this.localUserByIdCache = new MemoryKVCache<MiLocalUser>(Infinity);
|
||||||
// ローカルユーザーならlocalUserByIdCacheにデータを追加し、こちらにはid(文字列)だけを追加する
|
this.uriPersonCache = new MemoryKVCache<MiUser | null>(Infinity);
|
||||||
const userByIdCache = new MemoryKVCache<MiUser, MiUser | string>(1000 * 60 * 60 * 6 /* 6h */, {
|
|
||||||
toMapConverter: user => {
|
|
||||||
if (user.host === null) {
|
|
||||||
localUserByIdCache.set(user.id, user as MiLocalUser);
|
|
||||||
return user.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
},
|
|
||||||
fromMapConverter: userOrId => typeof userOrId === 'string' ? localUserByIdCache.get(userOrId) : userOrId,
|
|
||||||
});
|
|
||||||
this.userByIdCache = userByIdCache;
|
|
||||||
|
|
||||||
this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null, string | null>(Infinity, {
|
|
||||||
toMapConverter: user => {
|
|
||||||
if (user === null) return null;
|
|
||||||
|
|
||||||
localUserByIdCache.set(user.id, user);
|
|
||||||
return user.id;
|
|
||||||
},
|
|
||||||
fromMapConverter: id => id === null ? null : localUserByIdCache.get(id),
|
|
||||||
});
|
|
||||||
this.uriPersonCache = new MemoryKVCache<MiUser | null, string | null>(Infinity, {
|
|
||||||
toMapConverter: user => {
|
|
||||||
if (user === null) return null;
|
|
||||||
if (user.isDeleted) return null;
|
|
||||||
|
|
||||||
userByIdCache.set(user.id, user);
|
|
||||||
return user.id;
|
|
||||||
},
|
|
||||||
fromMapConverter: id => id === null ? null : userByIdCache.get(id),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', {
|
this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', {
|
||||||
lifetime: 1000 * 60 * 30, // 30m
|
lifetime: 1000 * 60 * 30, // 30m
|
||||||
|
@ -165,14 +133,14 @@ export class CacheService implements OnApplicationShutdown {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
this.userByIdCache.delete(body.id);
|
this.userByIdCache.delete(body.id);
|
||||||
for (const [k, v] of this.uriPersonCache.cache.entries()) {
|
for (const [k, v] of this.uriPersonCache.cache.entries()) {
|
||||||
if (v.value === body.id) {
|
if (v.value?.id === body.id) {
|
||||||
this.uriPersonCache.delete(k);
|
this.uriPersonCache.delete(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.userByIdCache.set(user.id, user);
|
this.userByIdCache.set(user.id, user);
|
||||||
for (const [k, v] of this.uriPersonCache.cache.entries()) {
|
for (const [k, v] of this.uriPersonCache.cache.entries()) {
|
||||||
if (v.value === user.id) {
|
if (v.value?.id === user.id) {
|
||||||
this.uriPersonCache.set(k, user);
|
this.uriPersonCache.set(k, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import FFmpeg from 'fluent-ffmpeg';
|
||||||
import isSvg from 'is-svg';
|
import isSvg from 'is-svg';
|
||||||
import probeImageSize from 'probe-image-size';
|
import probeImageSize from 'probe-image-size';
|
||||||
import { type predictionType } from 'nsfwjs';
|
import { type predictionType } from 'nsfwjs';
|
||||||
import sharp from 'sharp';
|
import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
|
||||||
import { encode } from 'blurhash';
|
import { encode } from 'blurhash';
|
||||||
import { createTempDir } from '@/misc/create-temp.js';
|
import { createTempDir } from '@/misc/create-temp.js';
|
||||||
import { AiService } from '@/core/AiService.js';
|
import { AiService } from '@/core/AiService.js';
|
||||||
|
@ -122,7 +122,7 @@ export class FileInfoService {
|
||||||
'image/avif',
|
'image/avif',
|
||||||
'image/svg+xml',
|
'image/svg+xml',
|
||||||
].includes(type.mime)) {
|
].includes(type.mime)) {
|
||||||
blurhash = await this.getBlurhash(path).catch(e => {
|
blurhash = await this.getBlurhash(path, type.mime).catch(e => {
|
||||||
warnings.push(`getBlurhash failed: ${e}`);
|
warnings.push(`getBlurhash failed: ${e}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
@ -407,25 +407,14 @@ export class FileInfoService {
|
||||||
* Calculate average color of image
|
* Calculate average color of image
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
private getBlurhash(path: string): Promise<string> {
|
private async getBlurhash(path: string, type: string): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
const sharp = await sharpBmp(path, type);
|
||||||
sharp(path)
|
const { data, info } = await sharp
|
||||||
.raw()
|
.raw()
|
||||||
.ensureAlpha()
|
.ensureAlpha()
|
||||||
.resize(64, 64, { fit: 'inside' })
|
.resize(64, 64, { fit: 'inside' })
|
||||||
.toBuffer((err, buffer, info) => {
|
.toBuffer({ resolveWithObject: true });
|
||||||
if (err) return reject(err);
|
|
||||||
|
|
||||||
let hash;
|
return encode(new Uint8ClampedArray(data), info.width, info.height, 5, 5);
|
||||||
|
|
||||||
try {
|
|
||||||
hash = encode(new Uint8ClampedArray(buffer), info.width, info.height, 5, 5);
|
|
||||||
} catch (e) {
|
|
||||||
return reject(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(hash);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,12 @@ import { RoleService } from '@/core/RoleService.js';
|
||||||
import { FeaturedService } from '@/core/FeaturedService.js';
|
import { FeaturedService } from '@/core/FeaturedService.js';
|
||||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||||
|
|
||||||
const FALLBACK = '❤';
|
const FALLBACK = '\u2764';
|
||||||
const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16;
|
const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16;
|
||||||
|
|
||||||
const legacies: Record<string, string> = {
|
const legacies: Record<string, string> = {
|
||||||
'like': '👍',
|
'like': '👍',
|
||||||
'love': '❤', // ここに記述する場合は異体字セレクタを入れない
|
'love': '\u2764', // ハート、異体字セレクタを入れない
|
||||||
'laugh': '😆',
|
'laugh': '😆',
|
||||||
'hmm': '🤔',
|
'hmm': '🤔',
|
||||||
'surprise': '😮',
|
'surprise': '😮',
|
||||||
|
@ -120,7 +120,7 @@ export class ReactionService {
|
||||||
let reaction = _reaction ?? FALLBACK;
|
let reaction = _reaction ?? FALLBACK;
|
||||||
|
|
||||||
if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && (user.host != null))) {
|
if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && (user.host != null))) {
|
||||||
reaction = '❤️';
|
reaction = '\u2764';
|
||||||
} else if (_reaction) {
|
} else if (_reaction) {
|
||||||
const custom = reaction.match(isCustomEmojiRegexp);
|
const custom = reaction.match(isCustomEmojiRegexp);
|
||||||
if (custom) {
|
if (custom) {
|
||||||
|
@ -327,35 +327,36 @@ export class ReactionService {
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文字列タイプのレガシーな形式のリアクションを現在の形式に変換しつつ、
|
||||||
|
* データベース上には存在する「0個のリアクションがついている」という情報を削除する。
|
||||||
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public convertLegacyReactions(reactions: Record<string, number>) {
|
public convertLegacyReactions(reactions: MiNote['reactions']): MiNote['reactions'] {
|
||||||
const _reactions = {} as Record<string, number>;
|
return Object.entries(reactions)
|
||||||
|
.filter(([, count]) => {
|
||||||
|
// `ReactionService.prototype.delete`ではリアクション削除時に、
|
||||||
|
// `MiNote['reactions']`のエントリの値をデクリメントしているが、
|
||||||
|
// デクリメントしているだけなのでエントリ自体は0を値として持つ形で残り続ける。
|
||||||
|
// そのため、この処理がなければ、「0個のリアクションがついている」ということになってしまう。
|
||||||
|
return count > 0;
|
||||||
|
})
|
||||||
|
.map(([reaction, count]) => {
|
||||||
|
// unchecked indexed access
|
||||||
|
const convertedReaction = legacies[reaction] as string | undefined;
|
||||||
|
|
||||||
for (const reaction of Object.keys(reactions)) {
|
const key = this.decodeReaction(convertedReaction ?? reaction).reaction;
|
||||||
if (reactions[reaction] <= 0) continue;
|
|
||||||
|
|
||||||
if (Object.keys(legacies).includes(reaction)) {
|
return [key, count] as const;
|
||||||
if (_reactions[legacies[reaction]]) {
|
})
|
||||||
_reactions[legacies[reaction]] += reactions[reaction];
|
.reduce<MiNote['reactions']>((acc, [key, count]) => {
|
||||||
} else {
|
// unchecked indexed access
|
||||||
_reactions[legacies[reaction]] = reactions[reaction];
|
const prevCount = acc[key] as number | undefined;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_reactions[reaction]) {
|
|
||||||
_reactions[reaction] += reactions[reaction];
|
|
||||||
} else {
|
|
||||||
_reactions[reaction] = reactions[reaction];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const _reactions2 = {} as Record<string, number>;
|
acc[key] = (prevCount ?? 0) + count;
|
||||||
|
|
||||||
for (const reaction of Object.keys(_reactions)) {
|
return acc;
|
||||||
_reactions2[this.decodeReaction(reaction).reaction] = _reactions[reaction];
|
}, {});
|
||||||
}
|
|
||||||
|
|
||||||
return _reactions2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
verifyRegistrationResponse,
|
verifyRegistrationResponse,
|
||||||
} from '@simplewebauthn/server';
|
} from '@simplewebauthn/server';
|
||||||
import { AttestationFormat, isoCBOR } from '@simplewebauthn/server/helpers';
|
import { AttestationFormat, isoCBOR } from '@simplewebauthn/server/helpers';
|
||||||
|
import { tinyCbor } from '@simplewebauthn/server/esm/deps.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UserSecurityKeysRepository } from '@/models/_.js';
|
import type { UserSecurityKeysRepository } from '@/models/_.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
|
@ -198,7 +199,7 @@ export class WebAuthnService {
|
||||||
if (cert[0] === 0x04) { // 前の実装ではいつも 0x04 で始まっていた
|
if (cert[0] === 0x04) { // 前の実装ではいつも 0x04 で始まっていた
|
||||||
const halfLength = (cert.length - 1) / 2;
|
const halfLength = (cert.length - 1) / 2;
|
||||||
|
|
||||||
const cborMap = new Map<number, number | ArrayBufferLike>();
|
const cborMap = new Map<number, tinyCbor.CBORType>();
|
||||||
cborMap.set(1, 2); // kty, EC2
|
cborMap.set(1, 2); // kty, EC2
|
||||||
cborMap.set(3, -7); // alg, ES256
|
cborMap.set(3, -7); // alg, ES256
|
||||||
cborMap.set(-1, 1); // crv, P256
|
cborMap.set(-1, 1); // crv, P256
|
||||||
|
|
|
@ -192,28 +192,14 @@ export class RedisSingleCache<T> {
|
||||||
|
|
||||||
// TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする?
|
// TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする?
|
||||||
|
|
||||||
function nothingToDo<T, V = T>(value: T): V {
|
export class MemoryKVCache<T> {
|
||||||
return value as unknown as V;
|
public cache: Map<string, { date: number; value: T; }>;
|
||||||
}
|
|
||||||
|
|
||||||
export class MemoryKVCache<T, V = T> {
|
|
||||||
public cache: Map<string, { date: number; value: V; }>;
|
|
||||||
private lifetime: number;
|
private lifetime: number;
|
||||||
private gcIntervalHandle: NodeJS.Timeout;
|
private gcIntervalHandle: NodeJS.Timeout;
|
||||||
private toMapConverter: (value: T) => V;
|
|
||||||
private fromMapConverter: (cached: V) => T | undefined;
|
|
||||||
|
|
||||||
constructor(lifetime: MemoryKVCache<never>['lifetime'], options: {
|
constructor(lifetime: MemoryKVCache<never>['lifetime']) {
|
||||||
toMapConverter: (value: T) => V;
|
|
||||||
fromMapConverter: (cached: V) => T | undefined;
|
|
||||||
} = {
|
|
||||||
toMapConverter: nothingToDo,
|
|
||||||
fromMapConverter: nothingToDo,
|
|
||||||
}) {
|
|
||||||
this.cache = new Map();
|
this.cache = new Map();
|
||||||
this.lifetime = lifetime;
|
this.lifetime = lifetime;
|
||||||
this.toMapConverter = options.toMapConverter;
|
|
||||||
this.fromMapConverter = options.fromMapConverter;
|
|
||||||
|
|
||||||
this.gcIntervalHandle = setInterval(() => {
|
this.gcIntervalHandle = setInterval(() => {
|
||||||
this.gc();
|
this.gc();
|
||||||
|
@ -224,7 +210,7 @@ export class MemoryKVCache<T, V = T> {
|
||||||
public set(key: string, value: T): void {
|
public set(key: string, value: T): void {
|
||||||
this.cache.set(key, {
|
this.cache.set(key, {
|
||||||
date: Date.now(),
|
date: Date.now(),
|
||||||
value: this.toMapConverter(value),
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +222,7 @@ export class MemoryKVCache<T, V = T> {
|
||||||
this.cache.delete(key);
|
this.cache.delete(key);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return this.fromMapConverter(cached.value);
|
return cached.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -247,10 +233,9 @@ export class MemoryKVCache<T, V = T> {
|
||||||
/**
|
/**
|
||||||
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
|
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
|
||||||
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
|
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
|
||||||
* fetcherの引数はcacheに保存されている値があれば渡されます
|
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetch(key: string, fetcher: (value: V | undefined) => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
|
public async fetch(key: string, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
|
||||||
const cachedValue = this.get(key);
|
const cachedValue = this.get(key);
|
||||||
if (cachedValue !== undefined) {
|
if (cachedValue !== undefined) {
|
||||||
if (validator) {
|
if (validator) {
|
||||||
|
@ -265,7 +250,7 @@ export class MemoryKVCache<T, V = T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache MISS
|
// Cache MISS
|
||||||
const value = await fetcher(this.cache.get(key)?.value);
|
const value = await fetcher();
|
||||||
this.set(key, value);
|
this.set(key, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -273,10 +258,9 @@ export class MemoryKVCache<T, V = T> {
|
||||||
/**
|
/**
|
||||||
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
|
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
|
||||||
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
|
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
|
||||||
* fetcherの引数はcacheに保存されている値があれば渡されます
|
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchMaybe(key: string, fetcher: (value: V | undefined) => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
|
public async fetchMaybe(key: string, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
|
||||||
const cachedValue = this.get(key);
|
const cachedValue = this.get(key);
|
||||||
if (cachedValue !== undefined) {
|
if (cachedValue !== undefined) {
|
||||||
if (validator) {
|
if (validator) {
|
||||||
|
@ -291,7 +275,7 @@ export class MemoryKVCache<T, V = T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache MISS
|
// Cache MISS
|
||||||
const value = await fetcher(this.cache.get(key)?.value);
|
const value = await fetcher();
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
this.set(key, value);
|
this.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
9
packages/backend/src/misc/fastify-hook-handlers.ts
Normal file
9
packages/backend/src/misc/fastify-hook-handlers.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import type { onRequestHookHandler } from 'fastify';
|
||||||
|
|
||||||
|
export const handleRequestRedirectToOmitSearch: onRequestHookHandler = (request, reply, done) => {
|
||||||
|
const index = request.url.indexOf('?');
|
||||||
|
if (~index) {
|
||||||
|
reply.redirect(301, request.url.slice(0, index));
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
};
|
|
@ -39,7 +39,17 @@ import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js'
|
||||||
import { packedUserListMembershipSchema, packedUserListSchema } from '@/models/json-schema/user-list.js';
|
import { packedUserListMembershipSchema, packedUserListSchema } from '@/models/json-schema/user-list.js';
|
||||||
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
|
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
|
||||||
import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
||||||
import { packedRoleLiteSchema, packedRoleSchema, packedRolePoliciesSchema } from '@/models/json-schema/role.js';
|
import {
|
||||||
|
packedRoleLiteSchema,
|
||||||
|
packedRoleSchema,
|
||||||
|
packedRolePoliciesSchema,
|
||||||
|
packedRoleCondFormulaLogicsSchema,
|
||||||
|
packedRoleCondFormulaValueNot,
|
||||||
|
packedRoleCondFormulaValueIsLocalOrRemoteSchema,
|
||||||
|
packedRoleCondFormulaValueCreatedSchema,
|
||||||
|
packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
|
||||||
|
packedRoleCondFormulaValueSchema,
|
||||||
|
} from '@/models/json-schema/role.js';
|
||||||
import { packedAdSchema } from '@/models/json-schema/ad.js';
|
import { packedAdSchema } from '@/models/json-schema/ad.js';
|
||||||
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
|
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
|
||||||
|
|
||||||
|
@ -86,6 +96,12 @@ export const refs = {
|
||||||
FlashLike: packedFlashLikeSchema,
|
FlashLike: packedFlashLikeSchema,
|
||||||
|
|
||||||
Signin: packedSigninSchema,
|
Signin: packedSigninSchema,
|
||||||
|
RoleCondFormulaLogics: packedRoleCondFormulaLogicsSchema,
|
||||||
|
RoleCondFormulaValueNot: packedRoleCondFormulaValueNot,
|
||||||
|
RoleCondFormulaValueIsLocalOrRemote: packedRoleCondFormulaValueIsLocalOrRemoteSchema,
|
||||||
|
RoleCondFormulaValueCreated: packedRoleCondFormulaValueCreatedSchema,
|
||||||
|
RoleCondFormulaFollowersOrFollowingOrNotes: packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
|
||||||
|
RoleCondFormulaValue: packedRoleCondFormulaValueSchema,
|
||||||
RoleLite: packedRoleLiteSchema,
|
RoleLite: packedRoleLiteSchema,
|
||||||
Role: packedRoleSchema,
|
Role: packedRoleSchema,
|
||||||
RolePolicies: packedRolePoliciesSchema,
|
RolePolicies: packedRolePoliciesSchema,
|
||||||
|
|
|
@ -48,7 +48,7 @@ export class MiBubbleGameRecord {
|
||||||
@Column('jsonb', {
|
@Column('jsonb', {
|
||||||
default: [],
|
default: [],
|
||||||
})
|
})
|
||||||
public logs: any[];
|
public logs: number[][];
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -258,6 +258,8 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public turnstileSecretKey: string | null;
|
public turnstileSecretKey: string | null;
|
||||||
|
|
||||||
|
// chaptcha系を追加した際にはnodeinfoのレスポンスに追加するのを忘れないようにすること
|
||||||
|
|
||||||
@Column('enum', {
|
@Column('enum', {
|
||||||
enum: ['none', 'all', 'local', 'remote'],
|
enum: ['none', 'all', 'local', 'remote'],
|
||||||
default: 'none',
|
default: 'none',
|
||||||
|
@ -362,9 +364,9 @@ export class MiMeta {
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024,
|
length: 1024,
|
||||||
default: 'https://github.com/misskey-dev/misskey',
|
default: 'https://github.com/misskey-dev/misskey',
|
||||||
nullable: false,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public repositoryUrl: string;
|
public repositoryUrl: string | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024,
|
length: 1024,
|
||||||
|
|
|
@ -69,7 +69,7 @@ type CondFormulaValueNotesMoreThanOrEq = {
|
||||||
value: number;
|
value: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RoleCondFormulaValue =
|
export type RoleCondFormulaValue = { id: string } & (
|
||||||
CondFormulaValueAnd |
|
CondFormulaValueAnd |
|
||||||
CondFormulaValueOr |
|
CondFormulaValueOr |
|
||||||
CondFormulaValueNot |
|
CondFormulaValueNot |
|
||||||
|
@ -82,7 +82,8 @@ export type RoleCondFormulaValue =
|
||||||
CondFormulaValueFollowingLessThanOrEq |
|
CondFormulaValueFollowingLessThanOrEq |
|
||||||
CondFormulaValueFollowingMoreThanOrEq |
|
CondFormulaValueFollowingMoreThanOrEq |
|
||||||
CondFormulaValueNotesLessThanOrEq |
|
CondFormulaValueNotesLessThanOrEq |
|
||||||
CondFormulaValueNotesMoreThanOrEq;
|
CondFormulaValueNotesMoreThanOrEq
|
||||||
|
);
|
||||||
|
|
||||||
@Entity('role')
|
@Entity('role')
|
||||||
export class MiRole {
|
export class MiRole {
|
||||||
|
|
|
@ -47,12 +47,12 @@ export const packedReversiGameLiteSchema = {
|
||||||
user1: {
|
user1: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'User',
|
ref: 'UserLite',
|
||||||
},
|
},
|
||||||
user2: {
|
user2: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'User',
|
ref: 'UserLite',
|
||||||
},
|
},
|
||||||
winnerId: {
|
winnerId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -62,7 +62,7 @@ export const packedReversiGameLiteSchema = {
|
||||||
winner: {
|
winner: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
ref: 'User',
|
ref: 'UserLite',
|
||||||
},
|
},
|
||||||
surrenderedUserId: {
|
surrenderedUserId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -165,12 +165,12 @@ export const packedReversiGameDetailedSchema = {
|
||||||
user1: {
|
user1: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'User',
|
ref: 'UserLite',
|
||||||
},
|
},
|
||||||
user2: {
|
user2: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'User',
|
ref: 'UserLite',
|
||||||
},
|
},
|
||||||
winnerId: {
|
winnerId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -180,7 +180,7 @@ export const packedReversiGameDetailedSchema = {
|
||||||
winner: {
|
winner: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
ref: 'User',
|
ref: 'UserLite',
|
||||||
},
|
},
|
||||||
surrenderedUserId: {
|
surrenderedUserId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -226,6 +226,9 @@ export const packedReversiGameDetailedSchema = {
|
||||||
items: {
|
items: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
map: {
|
map: {
|
||||||
|
|
|
@ -1,3 +1,129 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const packedRoleCondFormulaLogicsSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string', optional: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
enum: ['and', 'or'],
|
||||||
|
},
|
||||||
|
values: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
items: {
|
||||||
|
ref: 'RoleCondFormulaValue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedRoleCondFormulaValueNot = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string', optional: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
enum: ['not'],
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false,
|
||||||
|
ref: 'RoleCondFormulaValue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string', optional: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
enum: ['isLocal', 'isRemote'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedRoleCondFormulaValueCreatedSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string', optional: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
enum: [
|
||||||
|
'createdLessThan',
|
||||||
|
'createdMoreThan',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
sec: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedRoleCondFormulaFollowersOrFollowingOrNotesSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string', optional: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
enum: [
|
||||||
|
'followersLessThanOrEq',
|
||||||
|
'followersMoreThanOrEq',
|
||||||
|
'followingLessThanOrEq',
|
||||||
|
'followingMoreThanOrEq',
|
||||||
|
'notesLessThanOrEq',
|
||||||
|
'notesMoreThanOrEq',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedRoleCondFormulaValueSchema = {
|
||||||
|
type: 'object',
|
||||||
|
oneOf: [
|
||||||
|
{
|
||||||
|
ref: 'RoleCondFormulaLogics',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: 'RoleCondFormulaValueNot',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: 'RoleCondFormulaValueIsLocalOrRemote',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: 'RoleCondFormulaValueCreated',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: 'RoleCondFormulaFollowersOrFollowingOrNotes',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const packedRolePoliciesSchema = {
|
export const packedRolePoliciesSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -198,6 +324,7 @@ export const packedRoleSchema = {
|
||||||
condFormula: {
|
condFormula: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
ref: 'RoleCondFormulaValue',
|
||||||
},
|
},
|
||||||
isPublic: {
|
isPublic: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
|
|
@ -3,16 +3,38 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const notificationRecieveConfig = {
|
export const notificationRecieveConfig = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: true,
|
oneOf: [
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
properties: {
|
properties: {
|
||||||
type: {
|
type: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
nullable: false, optional: false,
|
nullable: false,
|
||||||
enum: ['all', 'following', 'follower', 'mutualFollow', 'list', 'never'],
|
enum: ['all', 'following', 'follower', 'mutualFollow', 'never'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
required: ['type'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false,
|
||||||
|
enum: ['list'],
|
||||||
|
},
|
||||||
|
userListId: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['type', 'userListId'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const packedUserLiteSchema = {
|
export const packedUserLiteSchema = {
|
||||||
|
@ -538,15 +560,20 @@ export const packedMeDetailedOnlySchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
properties: {
|
properties: {
|
||||||
app: notificationRecieveConfig,
|
note: { optional: true, ...notificationRecieveConfig },
|
||||||
quote: notificationRecieveConfig,
|
follow: { optional: true, ...notificationRecieveConfig },
|
||||||
reply: notificationRecieveConfig,
|
mention: { optional: true, ...notificationRecieveConfig },
|
||||||
follow: notificationRecieveConfig,
|
reply: { optional: true, ...notificationRecieveConfig },
|
||||||
renote: notificationRecieveConfig,
|
renote: { optional: true, ...notificationRecieveConfig },
|
||||||
mention: notificationRecieveConfig,
|
quote: { optional: true, ...notificationRecieveConfig },
|
||||||
reaction: notificationRecieveConfig,
|
reaction: { optional: true, ...notificationRecieveConfig },
|
||||||
pollEnded: notificationRecieveConfig,
|
pollEnded: { optional: true, ...notificationRecieveConfig },
|
||||||
receiveFollowRequest: notificationRecieveConfig,
|
receiveFollowRequest: { optional: true, ...notificationRecieveConfig },
|
||||||
|
followRequestAccepted: { optional: true, ...notificationRecieveConfig },
|
||||||
|
roleAssigned: { optional: true, ...notificationRecieveConfig },
|
||||||
|
achievementEarned: { optional: true, ...notificationRecieveConfig },
|
||||||
|
app: { optional: true, ...notificationRecieveConfig },
|
||||||
|
test: { optional: true, ...notificationRecieveConfig },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emailNotificationTypes: {
|
emailNotificationTypes: {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||||
import { correctFilename } from '@/misc/correct-filename.js';
|
import { correctFilename } from '@/misc/correct-filename.js';
|
||||||
|
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
|
||||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
||||||
|
|
||||||
const _filename = fileURLToPath(import.meta.url);
|
const _filename = fileURLToPath(import.meta.url);
|
||||||
|
@ -67,6 +68,8 @@ export class FileServerService {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fastify.register((fastify, options, done) => {
|
||||||
|
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
|
||||||
fastify.get('/files/app-default.jpg', (request, reply) => {
|
fastify.get('/files/app-default.jpg', (request, reply) => {
|
||||||
const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
|
const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
|
||||||
reply.header('Content-Type', 'image/jpeg');
|
reply.header('Content-Type', 'image/jpeg');
|
||||||
|
@ -79,8 +82,9 @@ export class FileServerService {
|
||||||
.catch(err => this.errorHandler(request, reply, err));
|
.catch(err => this.errorHandler(request, reply, err));
|
||||||
});
|
});
|
||||||
fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => {
|
fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => {
|
||||||
return await this.sendDriveFile(request, reply)
|
return await reply.redirect(301, `${this.config.url}/files/${request.params.key}`);
|
||||||
.catch(err => this.errorHandler(request, reply, err));
|
});
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get<{
|
fastify.get<{
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class NodeinfoServerService {
|
||||||
public getLinks() {
|
public getLinks() {
|
||||||
return [{
|
return [{
|
||||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
|
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
|
||||||
href: this.config.url + nodeinfo2_1path,
|
href: this.config.url + nodeinfo2_1path
|
||||||
}, {
|
}, {
|
||||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
||||||
href: this.config.url + nodeinfo2_0path,
|
href: this.config.url + nodeinfo2_0path,
|
||||||
|
@ -117,6 +117,8 @@ export class NodeinfoServerService {
|
||||||
emailRequiredForSignup: meta.emailRequiredForSignup,
|
emailRequiredForSignup: meta.emailRequiredForSignup,
|
||||||
enableHcaptcha: meta.enableHcaptcha,
|
enableHcaptcha: meta.enableHcaptcha,
|
||||||
enableRecaptcha: meta.enableRecaptcha,
|
enableRecaptcha: meta.enableRecaptcha,
|
||||||
|
enableMcaptcha: meta.enableMcaptcha,
|
||||||
|
enableTurnstile: meta.enableTurnstile,
|
||||||
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
||||||
enableEmail: meta.enableEmail,
|
enableEmail: meta.enableEmail,
|
||||||
enableServiceWorker: meta.enableServiceWorker,
|
enableServiceWorker: meta.enableServiceWorker,
|
||||||
|
|
|
@ -15,9 +15,6 @@ export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireAdmin: true,
|
requireAdmin: true,
|
||||||
kind: 'write:admin:delete-account',
|
kind: 'write:admin:delete-account',
|
||||||
|
|
||||||
res: {
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -84,6 +84,24 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
width: {
|
||||||
|
type: 'number',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: 'number',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
|
orientation: {
|
||||||
|
type: 'number',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
|
avgColor: {
|
||||||
|
type: 'string',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
storedInternal: {
|
storedInternal: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
|
|
@ -18,6 +18,18 @@ export const meta = {
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
additionalProperties: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
count: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['count', 'size'],
|
||||||
|
},
|
||||||
example: {
|
example: {
|
||||||
migrations: {
|
migrations: {
|
||||||
count: 66,
|
count: 66,
|
||||||
|
|
|
@ -454,7 +454,7 @@ export const meta = {
|
||||||
},
|
},
|
||||||
repositoryUrl: {
|
repositoryUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
summalyProxy: {
|
summalyProxy: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const meta = {
|
||||||
tags: ['admin', 'role', 'users'],
|
tags: ['admin', 'role', 'users'],
|
||||||
|
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
requireAdmin: true,
|
requireModerator: true,
|
||||||
kind: 'read:admin:roles',
|
kind: 'read:admin:roles',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
|
import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { notificationRecieveConfig } from '@/models/json-schema/user.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
@ -21,6 +22,157 @@ export const meta = {
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
|
properties: {
|
||||||
|
email: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
emailVerified: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
autoAcceptFollowed: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
noCrawle: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
preventAiLearning: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
alwaysMarkNsfw: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
autoSensitive: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
carefulBot: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
injectFeaturedNote: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
receiveAnnouncementEmail: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
mutedWords: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mutedInstances: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notificationRecieveConfig: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
note: { optional: true, ...notificationRecieveConfig },
|
||||||
|
follow: { optional: true, ...notificationRecieveConfig },
|
||||||
|
mention: { optional: true, ...notificationRecieveConfig },
|
||||||
|
reply: { optional: true, ...notificationRecieveConfig },
|
||||||
|
renote: { optional: true, ...notificationRecieveConfig },
|
||||||
|
quote: { optional: true, ...notificationRecieveConfig },
|
||||||
|
reaction: { optional: true, ...notificationRecieveConfig },
|
||||||
|
pollEnded: { optional: true, ...notificationRecieveConfig },
|
||||||
|
receiveFollowRequest: { optional: true, ...notificationRecieveConfig },
|
||||||
|
followRequestAccepted: { optional: true, ...notificationRecieveConfig },
|
||||||
|
roleAssigned: { optional: true, ...notificationRecieveConfig },
|
||||||
|
achievementEarned: { optional: true, ...notificationRecieveConfig },
|
||||||
|
app: { optional: true, ...notificationRecieveConfig },
|
||||||
|
test: { optional: true, ...notificationRecieveConfig },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isModerator: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
isSilenced: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
isSuspended: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
isHibernated: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
lastActiveDate: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
moderationNote: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
signins: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
ref: 'Signin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
policies: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'RolePolicies',
|
||||||
|
},
|
||||||
|
roles: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Role',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
roleAssigns: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
expiresAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
roleId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@ -92,7 +244,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
isLimited: isLimited,
|
isLimited: isLimited,
|
||||||
isSuspended: user.isSuspended,
|
isSuspended: user.isSuspended,
|
||||||
isHibernated: user.isHibernated,
|
isHibernated: user.isHibernated,
|
||||||
lastActiveDate: user.lastActiveDate,
|
lastActiveDate: user.lastActiveDate ? user.lastActiveDate.toISOString() : null,
|
||||||
moderationNote: profile.moderationNote ?? '',
|
moderationNote: profile.moderationNote ?? '',
|
||||||
signins,
|
signins,
|
||||||
policies: await this.roleService.getUserPolicies(user.id),
|
policies: await this.roleService.getUserPolicies(user.id),
|
||||||
|
|
|
@ -437,7 +437,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.repositoryUrl !== undefined) {
|
if (ps.repositoryUrl !== undefined) {
|
||||||
set.repositoryUrl = ps.repositoryUrl ?? 'https://github.com/misskey-dev/misskey';
|
set.repositoryUrl = URL.canParse(ps.repositoryUrl!) ? ps.repositoryUrl : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.feedbackUrl !== undefined) {
|
if (ps.feedbackUrl !== undefined) {
|
||||||
|
|
|
@ -24,9 +24,19 @@ export const meta = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
properties: {
|
properties: {
|
||||||
id: { type: 'string', format: 'misskey:id' },
|
id: {
|
||||||
score: { type: 'integer' },
|
type: 'string', format: 'misskey:id',
|
||||||
user: { ref: 'UserLite' },
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
score: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
ref: 'UserLite',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,9 +29,6 @@ export const meta = {
|
||||||
id: 'eb627bc7-574b-4a52-a860-3c3eae772b88',
|
id: 'eb627bc7-574b-4a52-a860-3c3eae772b88',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -39,7 +36,15 @@ export const paramDef = {
|
||||||
properties: {
|
properties: {
|
||||||
score: { type: 'integer', minimum: 0 },
|
score: { type: 'integer', minimum: 0 },
|
||||||
seed: { type: 'string', minLength: 1, maxLength: 1024 },
|
seed: { type: 'string', minLength: 1, maxLength: 1024 },
|
||||||
logs: { type: 'array' },
|
logs: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
gameMode: { type: 'string' },
|
gameMode: { type: 'string' },
|
||||||
gameVersion: { type: 'integer' },
|
gameVersion: { type: 'integer' },
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,6 +15,19 @@ export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
backupCodes: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -53,7 +53,7 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
nullable: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,21 +21,26 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
format: 'misskey:id',
|
format: 'misskey:id',
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
optional: true,
|
||||||
},
|
},
|
||||||
createdAt: {
|
createdAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
},
|
},
|
||||||
lastUsedAt: {
|
lastUsedAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
optional: true,
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
uniqueItems: true,
|
uniqueItems: true,
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|
|
@ -23,16 +23,19 @@ export const meta = {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'misskey:id',
|
format: 'misskey:id',
|
||||||
|
optional: false,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
},
|
},
|
||||||
callbackUrl: {
|
callbackUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
uniqueItems: true,
|
uniqueItems: true,
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -40,6 +43,7 @@ export const meta = {
|
||||||
},
|
},
|
||||||
isAuthorized: {
|
isAuthorized: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,6 +22,15 @@ export const meta = {
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
updatedAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
optional: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@ -50,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updatedAt: item.updatedAt,
|
updatedAt: item.updatedAt.toISOString(),
|
||||||
value: item.value,
|
value: item.value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,6 +13,9 @@ export const meta = {
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
additionalProperties: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,13 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
kind: 'read:account',
|
kind: 'read:account',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { safeForSql } from '@/misc/safe-for-sql.js';
|
import { safeForSql } from '@/misc/safe-for-sql.js';
|
||||||
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
|
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
|
||||||
|
import { notificationRecieveConfig } from '@/models/json-schema/user.js';
|
||||||
import { ApiLoggerService } from '../../ApiLoggerService.js';
|
import { ApiLoggerService } from '../../ApiLoggerService.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
|
@ -183,7 +184,26 @@ export const paramDef = {
|
||||||
mutedInstances: { type: 'array', items: {
|
mutedInstances: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
notificationRecieveConfig: { type: 'object' },
|
notificationRecieveConfig: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
note: notificationRecieveConfig,
|
||||||
|
follow: notificationRecieveConfig,
|
||||||
|
mention: notificationRecieveConfig,
|
||||||
|
reply: notificationRecieveConfig,
|
||||||
|
renote: notificationRecieveConfig,
|
||||||
|
quote: notificationRecieveConfig,
|
||||||
|
reaction: notificationRecieveConfig,
|
||||||
|
pollEnded: notificationRecieveConfig,
|
||||||
|
receiveFollowRequest: notificationRecieveConfig,
|
||||||
|
followRequestAccepted: notificationRecieveConfig,
|
||||||
|
roleAssigned: notificationRecieveConfig,
|
||||||
|
achievementEarned: notificationRecieveConfig,
|
||||||
|
app: notificationRecieveConfig,
|
||||||
|
test: notificationRecieveConfig,
|
||||||
|
},
|
||||||
|
},
|
||||||
emailNotificationTypes: { type: 'array', items: {
|
emailNotificationTypes: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
|
|
|
@ -109,7 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
url: webhook.url,
|
url: webhook.url,
|
||||||
secret: webhook.secret,
|
secret: webhook.secret,
|
||||||
active: webhook.active,
|
active: webhook.active,
|
||||||
latestSentAt: webhook.latestSentAt?.toISOString(),
|
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
|
||||||
latestStatus: webhook.latestStatus,
|
latestStatus: webhook.latestStatus,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
url: webhook.url,
|
url: webhook.url,
|
||||||
secret: webhook.secret,
|
secret: webhook.secret,
|
||||||
active: webhook.active,
|
active: webhook.active,
|
||||||
latestSentAt: webhook.latestSentAt?.toISOString(),
|
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
|
||||||
latestStatus: webhook.latestStatus,
|
latestStatus: webhook.latestStatus,
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
|
@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
url: webhook.url,
|
url: webhook.url,
|
||||||
secret: webhook.secret,
|
secret: webhook.secret,
|
||||||
active: webhook.active,
|
active: webhook.active,
|
||||||
latestSentAt: webhook.latestSentAt?.toISOString(),
|
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
|
||||||
latestStatus: webhook.latestStatus,
|
latestStatus: webhook.latestStatus,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,12 +69,12 @@ export const meta = {
|
||||||
},
|
},
|
||||||
repositoryUrl: {
|
repositoryUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: true,
|
||||||
default: 'https://github.com/misskey-dev/misskey',
|
default: 'https://github.com/misskey-dev/misskey',
|
||||||
},
|
},
|
||||||
feedbackUrl: {
|
feedbackUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: true,
|
||||||
default: 'https://github.com/misskey-dev/misskey/issues/new',
|
default: 'https://github.com/misskey-dev/misskey/issues/new',
|
||||||
},
|
},
|
||||||
defaultDarkTheme: {
|
defaultDarkTheme: {
|
||||||
|
|
|
@ -14,9 +14,6 @@ export const meta = {
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -30,6 +30,9 @@ export const meta = {
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: true,
|
||||||
|
ref: 'ReversiGameDetailed',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
|
@ -19,20 +19,24 @@ export const meta = {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'misskey:id',
|
format: 'misskey:id',
|
||||||
|
optional: true, nullable: false,
|
||||||
},
|
},
|
||||||
required: {
|
required: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
optional: true, nullable: false,
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
optional: true, nullable: false,
|
||||||
},
|
},
|
||||||
nullableDefault: {
|
nullableDefault: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'hello',
|
default: 'hello',
|
||||||
nullable: true,
|
optional: true, nullable: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
|
||||||
import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { deepClone } from '@/misc/clone.js';
|
import { deepClone } from '@/misc/clone.js';
|
||||||
|
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
@ -195,7 +196,7 @@ export class ClientServerService {
|
||||||
// Authenticate
|
// Authenticate
|
||||||
fastify.addHook('onRequest', async (request, reply) => {
|
fastify.addHook('onRequest', async (request, reply) => {
|
||||||
// %71ueueとかでリクエストされたら困るため
|
// %71ueueとかでリクエストされたら困るため
|
||||||
const url = decodeURI(request.routeOptions.url);
|
const url = decodeURI(request.routeOptions.url ?? '');
|
||||||
if (url === bullBoardPath || url.startsWith(bullBoardPath + '/')) {
|
if (url === bullBoardPath || url.startsWith(bullBoardPath + '/')) {
|
||||||
const token = request.cookies.token;
|
const token = request.cookies.token;
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
|
@ -266,12 +267,17 @@ export class ClientServerService {
|
||||||
|
|
||||||
//#region vite assets
|
//#region vite assets
|
||||||
if (this.config.clientManifestExists) {
|
if (this.config.clientManifestExists) {
|
||||||
|
fastify.register((fastify, options, done) => {
|
||||||
fastify.register(fastifyStatic, {
|
fastify.register(fastifyStatic, {
|
||||||
root: viteOut,
|
root: viteOut,
|
||||||
prefix: '/vite/',
|
prefix: '/vite/',
|
||||||
maxAge: ms('30 days'),
|
maxAge: ms('30 days'),
|
||||||
|
immutable: true,
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
});
|
});
|
||||||
|
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
|
||||||
|
done();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const port = (process.env.VITE_PORT ?? '5173');
|
const port = (process.env.VITE_PORT ?? '5173');
|
||||||
fastify.register(fastifyProxy, {
|
fastify.register(fastifyProxy, {
|
||||||
|
|
|
@ -90,4 +90,45 @@ describe('ReactionService', () => {
|
||||||
assert.strictEqual(await reactionService.normalize('unknown'), '❤');
|
assert.strictEqual(await reactionService.normalize('unknown'), '❤');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('convertLegacyReactions', () => {
|
||||||
|
test('空の入力に対しては何もしない', () => {
|
||||||
|
const input = {};
|
||||||
|
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Unicode絵文字リアクションを変換してしまわない', () => {
|
||||||
|
const input = { '👍': 1, '🍮': 2 };
|
||||||
|
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('カスタム絵文字リアクションを変換してしまわない', () => {
|
||||||
|
const input = { ':like@.:': 1, ':pudding@example.tld:': 2 };
|
||||||
|
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('文字列によるレガシーなリアクションを変換する', () => {
|
||||||
|
const input = { 'like': 1, 'pudding': 2 };
|
||||||
|
const output = { '👍': 1, '🍮': 2 };
|
||||||
|
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('host部分が省略されたレガシーなカスタム絵文字リアクションを変換する', () => {
|
||||||
|
const input = { ':custom_emoji:': 1 };
|
||||||
|
const output = { ':custom_emoji@.:': 1 };
|
||||||
|
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('「0個のリアクション」情報を削除する', () => {
|
||||||
|
const input = { 'angry': 0 };
|
||||||
|
const output = {};
|
||||||
|
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('host部分の有無によりデコードすると同じ表記になるカスタム絵文字リアクションの個数情報を正しく足し合わせる', () => {
|
||||||
|
const input = { ':custom_emoji:': 1, ':custom_emoji@.:': 2 };
|
||||||
|
const output = { ':custom_emoji@.:': 3 };
|
||||||
|
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,25 +3,28 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from 'node:path';
|
import { createRequire } from 'node:module';
|
||||||
|
import { dirname, join, resolve } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import type { StorybookConfig } from '@storybook/vue3-vite';
|
import type { StorybookConfig } from '@storybook/vue3-vite';
|
||||||
import { type Plugin, mergeConfig } from 'vite';
|
import { type Plugin, mergeConfig } from 'vite';
|
||||||
import turbosnap from 'vite-plugin-turbosnap';
|
import turbosnap from 'vite-plugin-turbosnap';
|
||||||
|
|
||||||
const dirname = fileURLToPath(new URL('.', import.meta.url));
|
const require = createRequire(import.meta.url);
|
||||||
|
const _dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||||
addons: [
|
addons: [
|
||||||
'@storybook/addon-essentials',
|
getAbsolutePath('@storybook/addon-essentials'),
|
||||||
'@storybook/addon-interactions',
|
getAbsolutePath('@storybook/addon-interactions'),
|
||||||
'@storybook/addon-links',
|
getAbsolutePath('@storybook/addon-links'),
|
||||||
'@storybook/addon-storysource',
|
getAbsolutePath('@storybook/addon-storysource'),
|
||||||
resolve(dirname, '../node_modules/storybook-addon-misskey-theme'),
|
getAbsolutePath('@storybook/addon-mdx-gfm'),
|
||||||
|
resolve(_dirname, '../node_modules/storybook-addon-misskey-theme'),
|
||||||
],
|
],
|
||||||
framework: {
|
framework: {
|
||||||
name: '@storybook/vue3-vite',
|
name: getAbsolutePath('@storybook/vue3-vite') as '@storybook/vue3-vite',
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
docs: {
|
docs: {
|
||||||
|
@ -37,10 +40,13 @@ const config = {
|
||||||
}
|
}
|
||||||
return mergeConfig(config, {
|
return mergeConfig(config, {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
// XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8
|
// XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8
|
||||||
(turbosnap as any as typeof turbosnap['default'])({
|
...(turbosnap as any as typeof turbosnap['default'])({
|
||||||
rootDir: config.root ?? process.cwd(),
|
rootDir: config.root ?? process.cwd(),
|
||||||
}),
|
}),
|
||||||
|
name: 'fake-turbosnap',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
target: [
|
target: [
|
||||||
|
@ -53,3 +59,7 @@ const config = {
|
||||||
},
|
},
|
||||||
} satisfies StorybookConfig;
|
} satisfies StorybookConfig;
|
||||||
export default config;
|
export default config;
|
||||||
|
|
||||||
|
function getAbsolutePath(value: string): string {
|
||||||
|
return dirname(require.resolve(join(value, 'package.json')));
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addons } from '@storybook/addons';
|
|
||||||
import { FORCE_REMOUNT } from '@storybook/core-events';
|
import { FORCE_REMOUNT } from '@storybook/core-events';
|
||||||
|
import { addons } from '@storybook/preview-api';
|
||||||
import { type Preview, setup } from '@storybook/vue3';
|
import { type Preview, setup } from '@storybook/vue3';
|
||||||
import isChromatic from 'chromatic/isChromatic';
|
import isChromatic from 'chromatic/isChromatic';
|
||||||
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
||||||
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||||
"build-storybook": "pnpm build-storybook-pre && storybook build",
|
"build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static",
|
||||||
"chromatic": "chromatic",
|
"chromatic": "chromatic",
|
||||||
"test": "vitest --run --globals",
|
"test": "vitest --run --globals",
|
||||||
"test-and-coverage": "vitest --run --coverage --globals",
|
"test-and-coverage": "vitest --run --coverage --globals",
|
||||||
|
@ -26,24 +26,24 @@
|
||||||
"@rollup/plugin-typescript": "11.1.6",
|
"@rollup/plugin-typescript": "11.1.6",
|
||||||
"@rollup/pluginutils": "5.1.0",
|
"@rollup/pluginutils": "5.1.0",
|
||||||
"@syuilo/aiscript": "0.17.0",
|
"@syuilo/aiscript": "0.17.0",
|
||||||
"@tabler/icons-webfont": "2.46.0",
|
"@tabler/icons-webfont": "2.47.0",
|
||||||
"@twemoji/parser": "15.0.0",
|
"@twemoji/parser": "15.0.0",
|
||||||
"@vitejs/plugin-vue": "5.0.3",
|
"@vitejs/plugin-vue": "5.0.4",
|
||||||
"@vue/compiler-sfc": "3.4.15",
|
"@vue/compiler-sfc": "3.4.15",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
||||||
"astring": "1.8.6",
|
"astring": "1.8.6",
|
||||||
"broadcast-channel": "7.0.0",
|
"broadcast-channel": "7.0.0",
|
||||||
"buraha": "0.0.1",
|
"buraha": "0.0.1",
|
||||||
"canvas-confetti": "1.6.1",
|
"canvas-confetti": "1.9.2",
|
||||||
"chart.js": "4.4.1",
|
"chart.js": "4.4.1",
|
||||||
"chartjs-adapter-date-fns": "3.0.0",
|
"chartjs-adapter-date-fns": "3.0.0",
|
||||||
"chartjs-chart-matrix": "2.0.1",
|
"chartjs-chart-matrix": "2.0.1",
|
||||||
"chartjs-plugin-gradient": "0.6.1",
|
"chartjs-plugin-gradient": "0.6.1",
|
||||||
"chartjs-plugin-zoom": "2.0.1",
|
"chartjs-plugin-zoom": "2.0.1",
|
||||||
"chromatic": "10.9.2",
|
"chromatic": "10.9.6",
|
||||||
"compare-versions": "6.1.0",
|
"compare-versions": "6.1.0",
|
||||||
"cropperjs": "2.0.0-beta.4",
|
"cropperjs": "2.0.0-beta.4",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "3.3.1",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"estree-walker": "3.0.3",
|
"estree-walker": "3.0.3",
|
||||||
"eventemitter3": "5.0.1",
|
"eventemitter3": "5.0.1",
|
||||||
|
@ -58,10 +58,10 @@
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"photoswipe": "5.4.3",
|
"photoswipe": "5.4.3",
|
||||||
"punycode": "2.3.1",
|
"punycode": "2.3.1",
|
||||||
"rollup": "4.9.6",
|
"rollup": "4.12.0",
|
||||||
"sanitize-html": "2.11.0",
|
"sanitize-html": "2.12.0",
|
||||||
"sass": "1.70.0",
|
"sass": "1.71.1",
|
||||||
"shiki": "1.0.0",
|
"shiki": "1.1.6",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.161.0",
|
"three": "0.161.0",
|
||||||
|
@ -71,51 +71,51 @@
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "5.3.3",
|
"typescript": "5.3.3",
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"v-code-diff": "1.7.2",
|
"v-code-diff": "1.9.0",
|
||||||
"vite": "5.1.0",
|
"vite": "5.1.4",
|
||||||
"vue": "3.4.15",
|
"vue": "3.4.15",
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@misskey-dev/summaly": "5.0.3",
|
"@misskey-dev/summaly": "5.0.3",
|
||||||
"@storybook/addon-actions": "7.6.13",
|
"@storybook/addon-actions": "8.0.0-beta.2",
|
||||||
"@storybook/addon-essentials": "7.6.13",
|
"@storybook/addon-essentials": "8.0.0-beta.2",
|
||||||
"@storybook/addon-interactions": "7.6.13",
|
"@storybook/addon-interactions": "8.0.0-beta.2",
|
||||||
"@storybook/addon-links": "7.6.13",
|
"@storybook/addon-links": "8.0.0-beta.2",
|
||||||
"@storybook/addon-storysource": "7.6.13",
|
"@storybook/addon-mdx-gfm": "8.0.0-beta.2",
|
||||||
"@storybook/addons": "7.6.13",
|
"@storybook/addon-storysource": "8.0.0-beta.2",
|
||||||
"@storybook/blocks": "7.6.13",
|
"@storybook/blocks": "8.0.0-beta.2",
|
||||||
"@storybook/core-events": "7.6.13",
|
"@storybook/components": "8.0.0-beta.2",
|
||||||
"@storybook/jest": "0.2.3",
|
"@storybook/core-events": "8.0.0-beta.2",
|
||||||
"@storybook/manager-api": "7.6.13",
|
"@storybook/manager-api": "8.0.0-beta.2",
|
||||||
"@storybook/preview-api": "7.6.13",
|
"@storybook/preview-api": "8.0.0-beta.2",
|
||||||
"@storybook/react": "7.6.13",
|
"@storybook/react": "8.0.0-beta.2",
|
||||||
"@storybook/react-vite": "7.6.13",
|
"@storybook/react-vite": "8.0.0-beta.2",
|
||||||
"@storybook/testing-library": "0.2.2",
|
"@storybook/test": "8.0.0-beta.2",
|
||||||
"@storybook/theming": "7.6.13",
|
"@storybook/theming": "8.0.0-beta.2",
|
||||||
"@storybook/types": "7.6.13",
|
"@storybook/types": "8.0.0-beta.2",
|
||||||
"@storybook/vue3": "7.6.13",
|
"@storybook/vue3": "8.0.0-beta.2",
|
||||||
"@storybook/vue3-vite": "7.6.13",
|
"@storybook/vue3-vite": "8.0.0-beta.2",
|
||||||
"@testing-library/vue": "8.0.2",
|
"@testing-library/vue": "8.0.2",
|
||||||
"@types/escape-regexp": "0.0.3",
|
"@types/escape-regexp": "0.0.3",
|
||||||
"@types/estree": "1.0.5",
|
"@types/estree": "1.0.5",
|
||||||
"@types/matter-js": "0.19.6",
|
"@types/matter-js": "0.19.6",
|
||||||
"@types/micromatch": "4.0.6",
|
"@types/micromatch": "4.0.6",
|
||||||
"@types/node": "20.11.17",
|
"@types/node": "20.11.19",
|
||||||
"@types/punycode": "2.1.3",
|
"@types/punycode": "2.1.4",
|
||||||
"@types/sanitize-html": "2.11.0",
|
"@types/sanitize-html": "2.11.0",
|
||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/uuid": "9.0.8",
|
"@types/uuid": "9.0.8",
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "6.21.0",
|
"@typescript-eslint/eslint-plugin": "7.0.2",
|
||||||
"@typescript-eslint/parser": "6.21.0",
|
"@typescript-eslint/parser": "7.0.2",
|
||||||
"@vitest/coverage-v8": "0.34.6",
|
"@vitest/coverage-v8": "0.34.6",
|
||||||
"@vue/runtime-core": "3.4.15",
|
"@vue/runtime-core": "3.4.15",
|
||||||
"acorn": "8.11.3",
|
"acorn": "8.11.3",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.6.4",
|
"cypress": "13.6.5",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-vue": "9.21.1",
|
"eslint-plugin-vue": "9.21.1",
|
||||||
|
@ -123,19 +123,19 @@
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "10.0.3",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.5",
|
"micromatch": "4.0.5",
|
||||||
"msw": "2.1.7",
|
"msw": "2.2.1",
|
||||||
"msw-storybook-addon": "2.0.0-beta.1",
|
"msw-storybook-addon": "2.0.0-beta.1",
|
||||||
"nodemon": "3.0.3",
|
"nodemon": "3.0.3",
|
||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"start-server-and-test": "2.0.3",
|
"start-server-and-test": "2.0.3",
|
||||||
"storybook": "7.6.13",
|
"storybook": "8.0.0-beta.2",
|
||||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vitest": "0.34.6",
|
"vitest": "0.34.6",
|
||||||
"vitest-fetch-mock": "0.2.2",
|
"vitest-fetch-mock": "0.2.2",
|
||||||
"vue-component-type-helpers": "^1.8.27",
|
"vue-component-type-helpers": "1.8.27",
|
||||||
"vue-eslint-parser": "9.4.2",
|
"vue-eslint-parser": "9.4.2",
|
||||||
"vue-tsc": "1.8.27"
|
"vue-tsc": "1.8.27"
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { alert, confirm, popup, post, toast } from '@/os.js';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import * as sound from '@/scripts/sound.js';
|
import * as sound from '@/scripts/sound.js';
|
||||||
import { $i, signout, updateAccount } from '@/account.js';
|
import { $i, signout, updateAccount } from '@/account.js';
|
||||||
|
import { fetchInstance, instance } from '@/instance.js';
|
||||||
import { ColdDeviceStorage, defaultStore } from '@/store.js';
|
import { ColdDeviceStorage, defaultStore } from '@/store.js';
|
||||||
import { makeHotkey } from '@/scripts/hotkey.js';
|
import { makeHotkey } from '@/scripts/hotkey.js';
|
||||||
import { reactionPicker } from '@/scripts/reaction-picker.js';
|
import { reactionPicker } from '@/scripts/reaction-picker.js';
|
||||||
|
@ -225,6 +226,13 @@ export async function mainBoot() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchInstance().then(() => {
|
||||||
|
const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read');
|
||||||
|
if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') {
|
||||||
|
popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if ('Notification' in window) {
|
if ('Notification' in window) {
|
||||||
// 許可を得ていなかったらリクエスト
|
// 許可を得ていなかったらリクエスト
|
||||||
if (Notification.permission === 'default') {
|
if (Notification.permission === 'default') {
|
||||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<div class="bcekxzvu _margin _panel">
|
<div class="bcekxzvu _margin _panel">
|
||||||
<div class="target">
|
<div class="target">
|
||||||
<MkA v-user-preview="report.targetUserId" class="info" :to="`/admin/user/${report.targetUserId}`">
|
<MkA v-user-preview="report.targetUserId" class="info" :to="`/admin/user/${report.targetUserId}`" :behavior="'window'">
|
||||||
<MkAvatar class="avatar" :user="report.targetUser" indicator/>
|
<MkAvatar class="avatar" :user="report.targetUser" indicator/>
|
||||||
<div class="names">
|
<div class="names">
|
||||||
<MkUserName class="name" :user="report.targetUser"/>
|
<MkUserName class="name" :user="report.targetUser"/>
|
||||||
|
@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<Mfm :text="report.comment"/>
|
<Mfm :text="report.comment"/>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link">@{{ report.reporter.username }}</MkA></div>
|
<div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link" :behavior="'window'">@{{ report.reporter.username }}</MkA></div>
|
||||||
<div v-if="report.assignee">
|
<div v-if="report.assignee">
|
||||||
{{ i18n.ts.moderator }}:
|
{{ i18n.ts.moderator }}:
|
||||||
<MkAcct :user="report.assignee"/>
|
<MkAcct :user="report.assignee"/>
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
|
|
|
@ -123,7 +123,7 @@ function callback(response?: string) {
|
||||||
function onReceivedMessage(message: MessageEvent) {
|
function onReceivedMessage(message: MessageEvent) {
|
||||||
if (message.data.token) {
|
if (message.data.token) {
|
||||||
if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) {
|
if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) {
|
||||||
callback(<string>message.data.token);
|
callback(message.data.token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,7 +240,7 @@ const render = () => {
|
||||||
},
|
},
|
||||||
external: externalTooltipHandler,
|
external: externalTooltipHandler,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (item) => chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString(),
|
label: (item) => `${item.dataset.label}: ${chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString()}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
zoom: props.detailed ? {
|
zoom: props.detailed ? {
|
||||||
|
|
|
@ -52,6 +52,7 @@ async function fetchLanguage(to: string): Promise<void> {
|
||||||
return bundle.id === language || bundle.aliases?.includes(language);
|
return bundle.id === language || bundle.aliases?.includes(language);
|
||||||
});
|
});
|
||||||
if (bundles.length > 0) {
|
if (bundles.length > 0) {
|
||||||
|
if (_DEV_) console.log(`Loading language: ${language}`);
|
||||||
await highlighter.loadLanguage(bundles[0].import);
|
await highlighter.loadLanguage(bundles[0].import);
|
||||||
codeLang.value = language;
|
codeLang.value = language;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { galleryPost } from '../../.storybook/fakes.js';
|
import { galleryPost } from '../../.storybook/fakes.js';
|
||||||
import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
|
import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
|
||||||
|
|
|
@ -28,7 +28,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { userName } from '@/filters/user.js';
|
import { userName } from '@/filters/user.js';
|
||||||
import MediaImage from '@/components/MkMediaImage.vue';
|
import MediaImage from '@/components/MkMediaImage.vue';
|
||||||
|
|
|
@ -16,9 +16,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
@closed="$emit('closed')"
|
@closed="$emit('closed')"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<template v-if="pageMetadata?.value">
|
<template v-if="pageMetadata">
|
||||||
<i v-if="pageMetadata.value.icon" :class="pageMetadata.value.icon" style="margin-right: 0.5em;"></i>
|
<i v-if="pageMetadata.icon" :class="pageMetadata.icon" style="margin-right: 0.5em;"></i>
|
||||||
<span>{{ pageMetadata.value.title }}</span>
|
<span>{{ pageMetadata.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ComputedRef, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
|
import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
|
||||||
import RouterView from '@/components/global/RouterView.vue';
|
import RouterView from '@/components/global/RouterView.vue';
|
||||||
import MkWindow from '@/components/MkWindow.vue';
|
import MkWindow from '@/components/MkWindow.vue';
|
||||||
import { popout as _popout } from '@/scripts/popout.js';
|
import { popout as _popout } from '@/scripts/popout.js';
|
||||||
|
@ -37,7 +37,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
import { url } from '@/config.js';
|
import { url } from '@/config.js';
|
||||||
import { useScrollPositionManager } from '@/nirax.js';
|
import { useScrollPositionManager } from '@/nirax.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
|
import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { openingWindowsCount } from '@/os.js';
|
import { openingWindowsCount } from '@/os.js';
|
||||||
import { claimAchievement } from '@/scripts/achievements.js';
|
import { claimAchievement } from '@/scripts/achievements.js';
|
||||||
import { getScrollContainer } from '@/scripts/scroll.js';
|
import { getScrollContainer } from '@/scripts/scroll.js';
|
||||||
|
@ -56,7 +56,7 @@ const routerFactory = useRouterFactory();
|
||||||
const windowRouter = routerFactory(props.initialPath);
|
const windowRouter = routerFactory(props.initialPath);
|
||||||
|
|
||||||
const contents = shallowRef<HTMLElement | null>(null);
|
const contents = shallowRef<HTMLElement | null>(null);
|
||||||
const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
const pageMetadata = ref<null | PageMetadata>(null);
|
||||||
const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
|
const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
|
||||||
const history = ref<{ path: string; key: any; }[]>([{
|
const history = ref<{ path: string; key: any; }[]>([{
|
||||||
path: windowRouter.getCurrentPath(),
|
path: windowRouter.getCurrentPath(),
|
||||||
|
@ -101,9 +101,11 @@ windowRouter.addListener('replace', ctx => {
|
||||||
windowRouter.init();
|
windowRouter.init();
|
||||||
|
|
||||||
provide('router', windowRouter);
|
provide('router', windowRouter);
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((metadataGetter) => {
|
||||||
|
const info = metadataGetter();
|
||||||
pageMetadata.value = info;
|
pageMetadata.value = info;
|
||||||
});
|
});
|
||||||
|
provideReactiveMetadata(pageMetadata);
|
||||||
provide('shouldOmitHeaderTitle', true);
|
provide('shouldOmitHeaderTitle', true);
|
||||||
provide('shouldHeaderThin', true);
|
provide('shouldHeaderThin', true);
|
||||||
provide('forceSpacerMin', true);
|
provide('forceSpacerMin', true);
|
||||||
|
|
|
@ -152,11 +152,11 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent): void {
|
||||||
icon: 'ti ti-crop',
|
icon: 'ti ti-crop',
|
||||||
action: () : void => { crop(file); },
|
action: () : void => { crop(file); },
|
||||||
}] : [], {
|
}] : [], {
|
||||||
|
type: 'divider',
|
||||||
|
}, {
|
||||||
text: i18n.ts.attachCancel,
|
text: i18n.ts.attachCancel,
|
||||||
icon: 'ti ti-circle-x',
|
icon: 'ti ti-circle-x',
|
||||||
action: () => { detachMedia(file.id); },
|
action: () => { detachMedia(file.id); },
|
||||||
}, {
|
|
||||||
type: 'divider',
|
|
||||||
}, {
|
}, {
|
||||||
text: i18n.ts.deleteFile,
|
text: i18n.ts.deleteFile,
|
||||||
icon: 'ti ti-trash',
|
icon: 'ti ti-trash',
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { onBeforeUnmount } from 'vue';
|
import { onBeforeUnmount } from 'vue';
|
||||||
import MkSignupServerRules from './MkSignupDialog.rules.vue';
|
import MkSignupServerRules from './MkSignupDialog.rules.vue';
|
||||||
|
|
110
packages/frontend/src/components/MkSourceCodeAvailablePopup.vue
Normal file
110
packages/frontend/src/components/MkSourceCodeAvailablePopup.vue
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="_panel _shadow" :class="$style.root">
|
||||||
|
<div :class="$style.icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-open-source" width="40" height="40" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M12 3a9 9 0 0 1 3.618 17.243l-2.193 -5.602a3 3 0 1 0 -2.849 0l-2.193 5.603a9 9 0 0 1 3.617 -17.244z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.main">
|
||||||
|
<div :class="$style.title">
|
||||||
|
<I18n :src="i18n.ts.aboutX" tag="span">
|
||||||
|
<template #x>
|
||||||
|
{{ instance.name ?? host }}
|
||||||
|
</template>
|
||||||
|
</I18n>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.text">
|
||||||
|
<I18n :src="i18n.ts._aboutMisskey.thisIsModifiedVersion" tag="span">
|
||||||
|
<template #name>
|
||||||
|
{{ instance.name ?? host }}
|
||||||
|
</template>
|
||||||
|
</I18n>
|
||||||
|
<I18n :src="i18n.ts.correspondingSourceIsAvailable" tag="span">
|
||||||
|
<template #anchor>
|
||||||
|
<MkA to="/about-misskey" class="_link">{{ i18n.ts.aboutMisskey }}</MkA>
|
||||||
|
</template>
|
||||||
|
</I18n>
|
||||||
|
</div>
|
||||||
|
<div class="_buttons">
|
||||||
|
<MkButton @click="close">{{ i18n.ts.gotIt }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="_button" :class="$style.close" @click="close"><i class="ti ti-x"></i></button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import { host } from '@/config.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { instance } from '@/instance.js';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
|
const emit = defineEmits<(ev: 'closed') => void>();
|
||||||
|
|
||||||
|
const zIndex = os.claimZIndex('low');
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
miLocalStorage.setItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read', 'true');
|
||||||
|
emit('closed');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
position: fixed;
|
||||||
|
z-index: v-bind(zIndex);
|
||||||
|
bottom: var(--margin);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: calc(100% - (var(--margin) * 2));
|
||||||
|
max-width: 500px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 25px;
|
||||||
|
width: 100px;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.icon {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 450px) {
|
||||||
|
.icon {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding: 25px 25px 25px 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin: 0.7em 0 1em 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, within } from '@storybook/test';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkA from './MkA.vue';
|
import MkA from './MkA.vue';
|
||||||
import { tick } from '@/scripts/test-utils.js';
|
import { tick } from '@/scripts/test-utils.js';
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, waitFor } from '@storybook/test';
|
||||||
import { waitFor } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkError from './MkError.vue';
|
import MkError from './MkError.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { within } from '@storybook/testing-library';
|
import { expect, within } from '@storybook/test';
|
||||||
import { expect } from '@storybook/jest';
|
|
||||||
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
|
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { waitFor } from '@storybook/testing-library';
|
import { waitFor } from '@storybook/test';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkPageHeader from './MkPageHeader.vue';
|
import MkPageHeader from './MkPageHeader.vue';
|
||||||
export const Empty = {
|
export const Empty = {
|
||||||
|
|
|
@ -11,18 +11,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/>
|
<div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/>
|
||||||
|
|
||||||
<template v-if="metadata">
|
<template v-if="pageMetadata">
|
||||||
<div v-if="!hideTitle" :class="$style.titleContainer" @click="top">
|
<div v-if="!hideTitle" :class="$style.titleContainer" @click="top">
|
||||||
<div v-if="metadata.avatar" :class="$style.titleAvatarContainer">
|
<div v-if="pageMetadata.avatar" :class="$style.titleAvatarContainer">
|
||||||
<MkAvatar :class="$style.titleAvatar" :user="metadata.avatar" indicator/>
|
<MkAvatar :class="$style.titleAvatar" :user="pageMetadata.avatar" indicator/>
|
||||||
</div>
|
</div>
|
||||||
<i v-else-if="metadata.icon" :class="[$style.titleIcon, metadata.icon]"></i>
|
<i v-else-if="pageMetadata.icon" :class="[$style.titleIcon, pageMetadata.icon]"></i>
|
||||||
|
|
||||||
<div :class="$style.title">
|
<div :class="$style.title">
|
||||||
<MkUserName v-if="metadata.userName" :user="metadata.userName" :nowrap="true"/>
|
<MkUserName v-if="pageMetadata.userName" :user="pageMetadata.userName" :nowrap="true"/>
|
||||||
<div v-else-if="metadata.title">{{ metadata.title }}</div>
|
<div v-else-if="pageMetadata.title">{{ pageMetadata.title }}</div>
|
||||||
<div v-if="metadata.subtitle" :class="$style.subtitle">
|
<div v-if="pageMetadata.subtitle" :class="$style.subtitle">
|
||||||
{{ metadata.subtitle }}
|
{{ pageMetadata.subtitle }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +46,7 @@ import tinycolor from 'tinycolor2';
|
||||||
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
|
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
|
||||||
import { scrollToTop } from '@/scripts/scroll.js';
|
import { scrollToTop } from '@/scripts/scroll.js';
|
||||||
import { globalEvents } from '@/events.js';
|
import { globalEvents } from '@/events.js';
|
||||||
import { injectPageMetadata } from '@/scripts/page-metadata.js';
|
import { injectReactiveMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
|
import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
|
||||||
import { PageHeaderItem } from '@/types/page-header.js';
|
import { PageHeaderItem } from '@/types/page-header.js';
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ const emit = defineEmits<{
|
||||||
(ev: 'update:tab', key: string);
|
(ev: 'update:tab', key: string);
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const metadata = injectPageMetadata();
|
const pageMetadata = injectReactiveMetadata();
|
||||||
|
|
||||||
const hideTitle = inject('shouldOmitHeaderTitle', false);
|
const hideTitle = inject('shouldOmitHeaderTitle', false);
|
||||||
const thin_ = props.thin || inject('shouldHeaderThin', false);
|
const thin_ = props.thin || inject('shouldHeaderThin', false);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/test';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkTime from './MkTime.vue';
|
import MkTime from './MkTime.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks.js';
|
import { commonHandlers } from '../../../.storybook/mocks.js';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/test';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../../.storybook/fakes.js';
|
import { userDetailed } from '../../../.storybook/fakes.js';
|
||||||
import MkUserName from './MkUserName.vue';
|
import MkUserName from './MkUserName.vue';
|
||||||
|
|
|
@ -18,7 +18,7 @@ export const langs = _LANGS_;
|
||||||
const preParseLocale = miLocalStorage.getItem('locale');
|
const preParseLocale = miLocalStorage.getItem('locale');
|
||||||
export let locale = preParseLocale ? JSON.parse(preParseLocale) : null;
|
export let locale = preParseLocale ? JSON.parse(preParseLocale) : null;
|
||||||
export const version = _VERSION_;
|
export const version = _VERSION_;
|
||||||
export const instanceName = siteName === 'Misskey' ? host : siteName;
|
export const instanceName = siteName === 'Misskey' || siteName == null ? host : siteName;
|
||||||
export const ui = miLocalStorage.getItem('ui');
|
export const ui = miLocalStorage.getItem('ui');
|
||||||
export const debug = miLocalStorage.getItem('debug') === 'true';
|
export const debug = miLocalStorage.getItem('debug') === 'true';
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,6 @@ export class UserPreview {
|
||||||
this.el.removeEventListener('mouseover', this.onMouseover);
|
this.el.removeEventListener('mouseover', this.onMouseover);
|
||||||
this.el.removeEventListener('mouseleave', this.onMouseleave);
|
this.el.removeEventListener('mouseleave', this.onMouseleave);
|
||||||
this.el.removeEventListener('click', this.onClick);
|
this.el.removeEventListener('click', this.onClick);
|
||||||
window.clearInterval(this.checkTimer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ type Keys =
|
||||||
'latestDonationInfoShownAt' |
|
'latestDonationInfoShownAt' |
|
||||||
'neverShowDonationInfo' |
|
'neverShowDonationInfo' |
|
||||||
'neverShowLocalOnlyInfo' |
|
'neverShowLocalOnlyInfo' |
|
||||||
|
'modifiedVersionMustProminentlyOfferInAgplV3Section13Read' |
|
||||||
'lastUsed' |
|
'lastUsed' |
|
||||||
'lang' |
|
'lang' |
|
||||||
'drafts' |
|
'drafts' |
|
||||||
|
|
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>{{ i18n.ts.youShouldUpgradeClient }}</div>
|
<div>{{ i18n.ts.youShouldUpgradeClient }}</div>
|
||||||
<MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton>
|
<MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton>
|
||||||
</template>
|
</template>
|
||||||
<div><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.ts.troubleshooting }}</MkA></div>
|
<div><MkLink url="https://misskey-hub.net/docs/for-users/resources/troubleshooting/" target="_blank">{{ i18n.ts.troubleshooting }}</MkLink></div>
|
||||||
<div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div>
|
<div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkLink from '@/components/MkLink.vue';
|
||||||
import { version } from '@/config.js';
|
import { version } from '@/config.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { unisonReload } from '@/scripts/unison-reload.js';
|
import { unisonReload } from '@/scripts/unison-reload.js';
|
||||||
|
@ -66,10 +67,10 @@ const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata(() => ({
|
||||||
title: i18n.ts.error,
|
title: i18n.ts.error,
|
||||||
icon: 'ti ti-alert-triangle',
|
icon: 'ti ti-alert-triangle',
|
||||||
});
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<FormLink to="https://github.com/misskey-dev/misskey" external>
|
<FormLink to="https://github.com/misskey-dev/misskey" external>
|
||||||
<template #icon><i class="ti ti-code"></i></template>
|
<template #icon><i class="ti ti-code"></i></template>
|
||||||
{{ i18n.ts._aboutMisskey.source }}
|
{{ i18n.ts._aboutMisskey.source }} ({{ i18n.ts._aboutMisskey.original }})
|
||||||
<template #suffix>GitHub</template>
|
<template #suffix>GitHub</template>
|
||||||
</FormLink>
|
</FormLink>
|
||||||
<FormLink to="https://crowdin.com/project/misskey" external>
|
<FormLink to="https://crowdin.com/project/misskey" external>
|
||||||
|
@ -46,6 +46,20 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</FormLink>
|
</FormLink>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
<FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey'">
|
||||||
|
<div class="_gaps_s">
|
||||||
|
<MkInfo>
|
||||||
|
{{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }}
|
||||||
|
</MkInfo>
|
||||||
|
<FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external>
|
||||||
|
<template #icon><i class="ti ti-code"></i></template>
|
||||||
|
{{ i18n.ts._aboutMisskey.source }}
|
||||||
|
</FormLink>
|
||||||
|
<MkInfo v-if="!instance.repositoryUrl" warn>
|
||||||
|
{{ i18n.ts.sourceCodeIsNotYetProvided }}
|
||||||
|
</MkInfo>
|
||||||
|
</div>
|
||||||
|
</FormSection>
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>{{ i18n.ts._aboutMisskey.projectMembers }}</template>
|
<template #label>{{ i18n.ts._aboutMisskey.projectMembers }}</template>
|
||||||
<div :class="$style.contributors">
|
<div :class="$style.contributors">
|
||||||
|
@ -118,9 +132,10 @@ import { version } from '@/config.js';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkLink from '@/components/MkLink.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import { physics } from '@/scripts/physics.js';
|
import { physics } from '@/scripts/physics.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { instance } from '@/instance.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
@ -363,10 +378,10 @@ const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata(() => ({
|
||||||
title: i18n.ts.aboutMisskey,
|
title: i18n.ts.aboutMisskey,
|
||||||
icon: null,
|
icon: null,
|
||||||
});
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -35,11 +35,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #icon><i class="ti ti-info-circle"></i></template>
|
<template #icon><i class="ti ti-info-circle"></i></template>
|
||||||
{{ i18n.ts.aboutMisskey }}
|
{{ i18n.ts.aboutMisskey }}
|
||||||
</FormLink>
|
</FormLink>
|
||||||
<FormLink :to="instance.repositoryUrl" external>
|
<FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external>
|
||||||
<template #icon><i class="ti ti-code"></i></template>
|
<template #icon><i class="ti ti-code"></i></template>
|
||||||
{{ i18n.ts.sourcecode }}
|
{{ i18n.ts.sourceCode }}
|
||||||
<template #suffix>GitHub</template>
|
<template #suffix>GitHub</template>
|
||||||
</FormLink>
|
</FormLink>
|
||||||
|
<MkInfo v-else warn>
|
||||||
|
{{ i18n.ts.sourceCodeIsNotYetProvided }}
|
||||||
|
</MkInfo>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
|
@ -145,6 +148,7 @@ import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import FormSplit from '@/components/form/split.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||||
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkInstanceStats from '@/components/MkInstanceStats.vue';
|
import MkInstanceStats from '@/components/MkInstanceStats.vue';
|
||||||
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
|
@ -193,10 +197,10 @@ const headerTabs = computed(() => [{
|
||||||
icon: 'ti ti-chart-line',
|
icon: 'ti ti-chart-line',
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(() => ({
|
||||||
title: i18n.ts.instanceInfo,
|
title: i18n.ts.instanceInfo,
|
||||||
icon: 'ti ti-info-circle',
|
icon: 'ti ti-info-circle',
|
||||||
})));
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -48,10 +48,10 @@ onDeactivated(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata(() => ({
|
||||||
title: i18n.ts.achievements,
|
title: i18n.ts.achievements,
|
||||||
icon: 'ti ti-medal',
|
icon: 'ti ti-medal',
|
||||||
});
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -140,10 +140,10 @@ const headerTabs = computed(() => [{
|
||||||
icon: 'ti ti-code',
|
icon: 'ti ti-code',
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(() => ({
|
||||||
title: file.value ? i18n.ts.file + ': ' + file.value.name : i18n.ts.file,
|
title: file.value ? `${i18n.ts.file}: ${file.value.name}` : i18n.ts.file,
|
||||||
icon: 'ti ti-file',
|
icon: 'ti ti-file',
|
||||||
})));
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -531,10 +531,10 @@ const headerTabs = computed(() => [{
|
||||||
icon: 'ti ti-code',
|
icon: 'ti ti-code',
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(() => ({
|
||||||
title: user.value ? acct(user.value) : i18n.ts.userInfo,
|
title: user.value ? acct(user.value) : i18n.ts.userInfo,
|
||||||
icon: 'ti ti-user-exclamation',
|
icon: 'ti ti-user-exclamation',
|
||||||
})));
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue