diff --git a/CHANGELOG.md b/CHANGELOG.md index c9bda3534..6705535f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,17 @@ You should also include the user name that made the change. --> +## 13.5.6 (2023/02/10) + +### Improvements +- 非ログイン時にMiAuthを踏んだ際にMiAuthであることを表示する +- /auth/のUIをアップデート +- 利用規約同意UIの調整 +- クロップ時の質問を分かりやすく + +### Bugfixes +- fix: prevent clipping audio plyr's tooltip + ## 13.5.4 (2023/02/09) ### Improvements diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 85c7a3e63..c1b272b26 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -257,6 +257,8 @@ noMoreHistory: "Kein weiterer Verlauf vorhanden" startMessaging: "Neuen Chat erstellen" nUsersRead: "Von {n} Benutzern gelesen" agreeTo: "Ich stimme {0} zu" +agreeBelow: "Ich stimme Untenstehendem zu" +basicNotesBeforeCreateAccount: "Wichtige Infos" tos: "Nutzungsbedingungen" start: "Anfangen" home: "Startseite" @@ -862,6 +864,8 @@ failedToFetchAccountInformation: "Benutzerkontoinformationen konnten nicht abgef rateLimitExceeded: "Versuchsanzahl überschritten" cropImage: "Bild zuschneiden" cropImageAsk: "Möchtest du das Bild zuschneiden?" +cropYes: "Zuschneiden" +cropNo: "Unbearbeitet verwenden" file: "Datei" recentNHours: "Letzten {n} Stunden" recentNDays: "Letzten {n} Tage" @@ -940,6 +944,8 @@ cannotPerformTemporaryDescription: "Diese Aktion ist wegen des Überschreitenes preset: "Vorlage" selectFromPresets: "Aus Vorlagen wählen" achievements: "Errungenschaften" +gotInvalidResponseError: "Ungültige Antwort des Servers" +gotInvalidResponseErrorDescription: "Eventuell ist der Server momentan nicht erreichbar oder untergeht Wartungsarbeiten. Bitte versuche es später noch einmal." _achievements: earnedAt: "Freigeschaltet am" _types: @@ -1594,12 +1600,15 @@ _permissions: "read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen" "write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten" _auth: + shareAccessTitle: "Verteilung von App-Berechtigungen" shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?" shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?" + permission: "{name} fordert folgende Berechtigungen" permissionAsk: "Diese Anwendung fordert folgende Berechtigungen" pleaseGoBack: "Bitte kehre zur Anwendung zurück" callback: "Es wird zur Anwendung zurückgekehrt" denied: "Zugriff verweigert" + pleaseLogin: "Bitte logge dich ein, um Apps zu authorisieren." _antennaSources: all: "Alle Notizen" homeTimeline: "Notizen von Benutzern, denen gefolgt wird" diff --git a/locales/en-US.yml b/locales/en-US.yml index d9a34b899..685e373a4 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -257,6 +257,8 @@ noMoreHistory: "There is no further history" startMessaging: "Start a new chat" nUsersRead: "read by {n}" agreeTo: "I agree to {0}" +agreeBelow: "I agree to the below" +basicNotesBeforeCreateAccount: "Important notes" tos: "Terms of Service" start: "Begin" home: "Home" @@ -862,6 +864,8 @@ failedToFetchAccountInformation: "Could not fetch account information" rateLimitExceeded: "Rate limit exceeded" cropImage: "Crop image" cropImageAsk: "Do you want to crop this image?" +cropYes: "Crop" +cropNo: "Use as-is" file: "File" recentNHours: "Last {n} hours" recentNDays: "Last {n} days" @@ -940,6 +944,8 @@ cannotPerformTemporaryDescription: "This action cannot be performed temporarily preset: "Preset" selectFromPresets: "Choose from presets" achievements: "Achievements" +gotInvalidResponseError: "Invalid server response" +gotInvalidResponseErrorDescription: "The server may be unreachable or undergoing maintenance. Please try again later." _achievements: earnedAt: "Unlocked at" _types: @@ -1594,12 +1600,15 @@ _permissions: "read:gallery-likes": "View your list of liked gallery posts" "write:gallery-likes": "Edit your list of liked gallery posts" _auth: + shareAccessTitle: "Granting application permissions" shareAccess: "Would you like to authorize \"{name}\" to access this account?" shareAccessAsk: "Are you sure you want to authorize this application to access your account?" + permission: "{name} requests the following permissions" permissionAsk: "This application requests the following permissions" pleaseGoBack: "Please go back to the application" callback: "Returning to the application" denied: "Access denied" + pleaseLogin: "Please log in to authorize applications." _antennaSources: all: "All notes" homeTimeline: "Notes from followed users" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index f3cd85e6a..5c1d249f6 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -56,7 +56,7 @@ reply: "Responder" loadMore: "Ver más" showMore: "Ver más" showLess: "Cerrar" -youGotNewFollower: "te ha seguido" +youGotNewFollower: "ahora te sigue" receiveFollowRequest: "Recibiste una solicitud de seguimiento" followRequestAccepted: "La solicitud de seguimiento fue aceptada" mention: "Menciones" @@ -257,6 +257,8 @@ noMoreHistory: "El historial se ha acabado" startMessaging: "Iniciar chat" nUsersRead: "Leído por {n} personas" agreeTo: "De acuerdo con {0}" +agreeBelow: "Estoy de acuerdo con lo siguiente" +basicNotesBeforeCreateAccount: "Notas básicas" tos: "Términos de uso" start: "Comenzar" home: "Inicio" @@ -940,6 +942,8 @@ cannotPerformTemporaryDescription: "Esta acción no se puede realizar porque se preset: "Predefinido" selectFromPresets: "Escoger desde predefinidos" achievements: "Logros" +gotInvalidResponseError: "Respuesta del servidor inválida" +gotInvalidResponseErrorDescription: "Puede que el servidor esté caído o en mantenimiento. Favor de intentar más tarde" _achievements: earnedAt: "Desbloqueado el" _types: @@ -1594,12 +1598,15 @@ _permissions: "read:gallery-likes": "Ver favoritos de la galería" "write:gallery-likes": "Editar favoritos de la galería" _auth: + shareAccessTitle: "Permisos de la aplicación" shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?" shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder a su cuenta?" + permission: "{name} solicita los siguientes permisos" permissionAsk: "Esta aplicación requiere los siguientes permisos" pleaseGoBack: "Por favor, vuelve a la aplicación" callback: "Volviendo a la aplicación" denied: "Acceso denegado" + pleaseLogin: "Se requiere un inicio de sesión para darle permisos a la aplicación" _antennaSources: all: "Todas las notas" homeTimeline: "Notas de los usuarios que sigues" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 09069e780..79b78f3f9 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -257,6 +257,8 @@ noMoreHistory: "これより過去の履歴はありません" startMessaging: "チャットを開始" nUsersRead: "{n}人が読みました" agreeTo: "{0}に同意" +agreeBelow: "下記に同意する" +basicNotesBeforeCreateAccount: "基本的な注意事項" tos: "利用規約" start: "始める" home: "ホーム" @@ -862,6 +864,8 @@ failedToFetchAccountInformation: "アカウント情報の取得に失敗しま rateLimitExceeded: "レート制限を超えました" cropImage: "画像のクロップ" cropImageAsk: "画像をクロップしますか?" +cropYes: "クロップする" +cropNo: "そのまま使う" file: "ファイル" recentNHours: "直近{n}時間" recentNDays: "直近{n}日" @@ -1628,12 +1632,15 @@ _permissions: "write:gallery-likes": "ギャラリーのいいねを操作する" _auth: + shareAccessTitle: "アプリへのアクセス許可" shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?" shareAccessAsk: "アカウントへのアクセスを許可しますか?" + permission: "{name}は次の権限を要求しています" permissionAsk: "このアプリは次の権限を要求しています" pleaseGoBack: "アプリケーションに戻ってやっていってください" callback: "アプリケーションに戻っています" denied: "アクセスを拒否しました" + pleaseLogin: "アプリケーションにアクセス許可を与えるには、ログインが必要です。" _antennaSources: all: "全てのノート" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 05de911be..2987112e2 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -46,7 +46,7 @@ copyContent: "内容をコピー" copyLink: "リンクをコピー" delete: "ほかす" deleteAndEdit: "ほかして直す" -deleteAndEditConfirm: "このノートをほかして書き直すんか?このノートへのリアクション、Renote、返信も全部消えてまうで。" +deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのリアクション、Renote、返信も全部消えるんやけどそれでもええん?" addToList: "リストに入れたる" sendMessage: "メッセージを送る" copyRSS: "RSSをコピー" @@ -89,7 +89,7 @@ serverIsDead: "サーバーからの応答がないで。もうちょい待っ youShouldUpgradeClient: "このページを表示するには、リロードして新しいバージョンのクライアントを使ってなー。" enterListName: "リスト名を入れてや" privacy: "プライバシー" -makeFollowManuallyApprove: "自分が認めた人だけがこのアカウントをフォローできるようにする" +makeFollowManuallyApprove: "他人のフォローは許可してからや!" defaultNoteVisibility: "もとからの公開範囲" follow: "フォロー" followRequest: "フォローを頼む" @@ -129,6 +129,7 @@ unblockConfirm: "ブロックやめたるってほんまか?" suspendConfirm: "凍結してしもうてええか?" unsuspendConfirm: "解凍するけどええか?" selectList: "リストを選ぶ" +selectChannel: "チャンネルを選ぶ" selectAntenna: "アンテナを選ぶ" selectWidget: "ウィジェットを選ぶ" editWidgets: "ウィジェットをいじる" @@ -256,6 +257,8 @@ noMoreHistory: "これより過去の履歴はあらへんで" startMessaging: "チャットやるで" nUsersRead: "{n}人が読んでもうた" agreeTo: "{0}に同意したで" +agreeBelow: "下記に同意したる" +basicNotesBeforeCreateAccount: "よう読んでやってや" tos: "利用規約" start: "始める" home: "ホーム" @@ -300,7 +303,7 @@ avatar: "アイコン" banner: "バナー" nsfw: "閲覧注意" whenServerDisconnected: "サーバーとの接続が切れたとき" -disconnectedFromServer: "サーバーとの通信が切れたで" +disconnectedFromServer: "サーバーが機嫌悪いねん" reload: "リロード" doNothing: "何もせんとく" reloadConfirm: "リロードしてええか?" @@ -673,8 +676,8 @@ sentReactionsCount: "リアクションした数やで" receivedReactionsCount: "リアクションされた数" pollVotesCount: "アンケートに投票した数" pollVotedCount: "アンケートに投票された数" -yes: "はい" -no: "いいえ" +yes: "ええで" +no: "あかんで" driveFilesCount: "ドライブのファイル数" driveUsage: "ドライブ使用量やで" noCrawle: "クローラーによるインデックスを拒否するで" @@ -861,6 +864,8 @@ failedToFetchAccountInformation: "アカウントの取得に失敗したみた rateLimitExceeded: "レート制限が超えたみたいやで" cropImage: "画像のクロップ" cropImageAsk: "画像をクロップしたってええか?" +cropYes: "切り抜いたる" +cropNo: "切り抜かへん" file: "ファイル" recentNHours: "直近{n}時間" recentNDays: "直近{n}日" @@ -938,6 +943,37 @@ cannotPerformTemporary: "一時的に利用できへんで" cannotPerformTemporaryDescription: "操作回数が制限を超えたから一時的に利用できへんくなったで。ちょっと時間置いてからもう一回やってやー。" preset: "プリセット" selectFromPresets: "プリセットから選ぶ" +achievements: "実績" +gotInvalidResponseError: "サーバー黙っとるわ、知らんけど" +gotInvalidResponseErrorDescription: "サーバーいま日曜日。またきて月曜日。" +_achievements: + earnedAt: "貰った日ぃ" + _types: + _notes1: + title: "まいど!" + description: "初めてノート投稿したった" + _notes10: + title: "ノートの天保山" + _notes100: + title: "ノートの真田山" + _notes500: + title: "ノートの生駒山" + _notes5000: + title: "箕面の滝からノート" + _login3: + flavor: "今日からワシはミスキストやで" + _iLoveMisskey: + title: "Misskey好きやねん" + _foundTreasure: + title: "なんでも鑑定団" + _client30min: + title: "ねんね" + _noteDeletedWithin1min: + title: "*おおっと*" + _open3windows: + title: "マド開けすぎ" + _driveFolderCircularReference: + title: "環状線" _role: new: "ロールの作成" edit: "ロールの編集" @@ -1355,10 +1391,12 @@ _permissions: _auth: shareAccess: "「{name}」がアカウントにアクセスすることを許可してええか?" shareAccessAsk: "アカウントのアクセスを許可してもええか?" + permission: "{name}に次の権限つけたってやって" permissionAsk: "このアプリは次の権限を要求しとるで" pleaseGoBack: "アプリケーションに戻ってええよ" callback: "アプリケーションに戻っとるで" denied: "アクセスを拒否ったで" + pleaseLogin: "アプリにアクセスさせるんやったら、ログインしてや。" _antennaSources: all: "みんなのノート" homeTimeline: "フォローしとるユーザーのノート" @@ -1587,6 +1625,7 @@ _notification: pollEnded: "アンケートの結果が出たみたいや" unreadAntennaNote: "アンテナ {name}" emptyPushNotificationMessage: "プッシュ通知の更新をしといたで" + achievementEarned: "実績を獲得しとるで" _types: all: "すべて" follow: "フォロー" diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml index befb2eb36..cfda4e0e9 100644 --- a/locales/lo-LA.yml +++ b/locales/lo-LA.yml @@ -99,18 +99,84 @@ followRequestPending: "ປະຕິບັດຕາມຄໍາຮ້ອງຂໍ enterEmoji: "ປ້ອນອີໂມຈິ" renote: "Renote" unrenote: "ເລີກ Renote" +renoted: "ເກັບບັນທຶກໄວ້" +quote: "ລວມຂໍ້ຄວາມອ້າງອີງ" +pinnedNote: "ບັນທຶກທີ່ປັກໝຸດໄວ້" pinned: "ປັກໝຸດໄປຫາໂປຣໄຟລ໌" +you: "ເຈົ້າ" +clickToShow: "ກົດເພື່ອສະແດງໃຫ້ເຫັນ" +sensitive: "NSFW" +add: "ເພີ່ມ" +reaction: "ປະຕິກິລິຍາ" +reactions: "ປະຕິກິລິຍາ" +mute: "ປີດສຽງ" +unmute: "ເປີດສຽງ" +block: "ບ໋ອກ" +unblock: "ຍົກເລີກກາຮົບລັອກ" +suspend: "ລະງັບ" +unsuspend: "ເຊົາລະງັບ" +selectList: "ເລືອກບັນຊີລາຍການ" +selectWidget: "ເລືອກວິກເຈັດ" +editWidgets: "ແກ້ໄຂ Widget" +editWidgetsExit: "ສຳເລັດແລ້ວ" +customEmojis: "ອີໂມຈິແບບກຳນົດເອງ" +emoji: "ອີໂມຈິ" +emojis: "ອີໂມຈິ" +emojiName: "ຊື່ Emoji" +emojiUrl: "URL ອີໂມຈິ" +addEmoji: "ຕື່ມອີໂມຈິ" +flagAsBot: "ໝາຍບັນຊີນີ້ເປັນບັອດ" +flagAsCat: "ໝາຍບັນຊີນີ້ເປັນແມວ" +flagAsCatDescription: "ເປີດໃຊ້ຕົວເລືອກນີ້ເພື່ອໝາຍບັນຊີນີ້ເປັນແມວ" +flagShowTimelineReplies: "ສະແດງການຕອບກັບໃນທາມລາຍ" +flagShowTimelineRepliesDescription: "ສະແດງການຕອບກັບຂອງຜູ້ໃຊ້ຕໍ່ກັບບັນທຶກຂອງຜູ້ໃຊ້ອື່ນໃນທາມລາຍຖ້າເປີດໃຊ້ງານ" +autoAcceptFollowed: "ອະນຸມັດອັດຕະໂນມັດຕາມຄຳຮ້ອງຂໍຈາກຜູ້ໃຊ້ທີ່ທ່ານກຳລັງຕິດຕາມຢູ່" addAccount: "ເພີ່ມບັນຊີ" loginFailed: "ການເຂົ້າສູ່ລະບົບບໍ່ສຳເລັດ" general: "ທົ່ວໄປ" wallpaper: "ພາບພື້ນຫລັງ" setWallpaper: "ຕັ້ງເປັນພາບພື້ນຫຼັງ" instances: "ອີນສະແຕນ" +instanceInfo: "ອີນສະແຕນ" statistics: "ສະຖິຕິ" clearQueue: "ລ້າງຄິວ" clearCachedFiles: "ລຶບລ້າງແຄສ" editProfile: "ແກ້ໄຂໂປຣໄຟລ໌" +done: "ສຳເລັດ" +processing: "ກຳລັງປະມວນຜົນ" +preview: "ສະແດງເປັນຕົວຢ່າງ" +default: "ຄ່າເລີ່ມຕົ້ນ" +blocked: "ບລັອກແລ້ວ " +all: "ທັງໝົດ" +subscribing: "ສະໝັກສະມາຊິກແລັວ" +publishing: "ການພິມເຜີຍແຜ່" +notResponding: "ບໍ່ຕອບສະໜອງ" +instanceFollowing: "ກຳລັງຕິດຕາມສຸດຕົວຢ່າງ" +instanceFollowers: "ຜູ້ຕິດຕາມຕົວຢ່າງ" +instanceUsers: "ຜູ້ຊົມໃຊ້ຂອງຕົວຢ່າງນີ້" +changePassword: "ປ່ຽນລະຫັດຜ່ານ" +featured: "ໄຮໄລທ໌" +announcements: "ປະກາດ" remove: "ລຶບ" +messaging: "ແຊ໋ດ" +tos: "ເງື່ອນໄຂການໃຫ້ບໍລິການ" +start: "ເລີ່ມຕົ້ນນຳໃຊ້ເລີຍ" +home: "ໜ້າຫຼັກ" +images: "ຮູບພາບ" +birthday: "ວັນເກີດ" +registeredDate: "ວັນທີ່ເປັນສະມາຊິກ" +location: "ທີ່ຕັ້ງ" +theme: "ແທ໋ມ" +light: "ສະຫວ່າງ" +dark: "ມືດ" +lightThemes: "ຊຸດຮູບແບບສະຫວ່າງ" +darkThemes: "ຮູບແບບສີສັນມືດ" +fileName: "ຊື່ໄຟລ໌" +selectFile: "ເລືອກໄຟລ໌" +selectFiles: "ເລືອກໄຟລ໌" +nsfw: "NSFW" +accept: "ອະນຸຍາດ" +pinnedNotes: "ບັນທຶກທີ່ປັກໝຸດໄວ້" userList: "ລາຍການ" smtpUser: "ຊື່ຜູ້ໃຊ້" smtpPass: "ລະຫັດຜ່ານ" @@ -123,6 +189,8 @@ _email: title: "ໄດ້ຕິດຕາມທ່ານ" _mfm: mention: "ໄດ້ກ່າວມາ" + quote: "ລວມຂໍ້ຄວາມອ້າງອີງ" + emoji: "ອີໂມຈິແບບກຳນົດເອງ" search: "ຄົ້ນຫາ" _theme: keys: @@ -131,25 +199,39 @@ _theme: _sfx: note: "ບັນທຶກ" notification: "ການແຈ້ງເຕືອນ" + chat: "ແຊ໋ດ" _widgets: profile: "ໂພຼຟາຍ" + instanceInfo: "ອີນສະແຕນ" notifications: "ການແຈ້ງເຕືອນ" timeline: "ເສັ້ນກຳນົດເວລາ" + _userList: + chooseList: "ເລືອກບັນຊີລາຍການ" _cw: show: "ໂຫຼດເພີ່ມເຕີມ" _visibility: + home: "ໜ້າຫຼັກ" followers: "ຜູ້ຕິດຕາມ" _profile: username: "ຊື່ຜູ້ໃຊ້" _exportOrImport: followingList: "ກຳລັງຕິດຕາມ" + muteList: "ປີດສຽງ" + blockingList: "ບ໋ອກ" userLists: "ລາຍການ" +_timelines: + home: "ໜ້າຫຼັກ" +_pages: + blocks: + image: "ຮູບພາບ" _notification: youWereFollowed: "ໄດ້ຕິດຕາມທ່ານ" _types: follow: "ກຳລັງຕິດຕາມ" mention: "ໄດ້ກ່າວມາ" renote: "Renote" + quote: "ລວມຂໍ້ຄວາມອ້າງອີງ" + reaction: "ປະຕິກິລິຍາ" _actions: reply: "ຕອບໄປທີ" renote: "Renote" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 0f5ff6248..010e32426 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -940,6 +940,8 @@ cannotPerformTemporaryDescription: "การดําเนินการน preset: "พรีเซ็ต" selectFromPresets: "เลือกจากการพรีเซ็ต" achievements: "ความสำเร็จ" +gotInvalidResponseError: "การตอบสนองเซิร์ฟเวอร์ไม่ถูกต้อง" +gotInvalidResponseErrorDescription: "เซิร์ฟเวอร์อาจไม่สามารถเข้าถึงได้หรืออาจจะกำลังอยู่ในระหว่างปรับปรุง กรุณาลองใหม่อีกครั้งในภายหลังนะคะ" _achievements: earnedAt: "ได้รับเมื่อ" _types: @@ -1594,12 +1596,15 @@ _permissions: "read:gallery-likes": "ดูรายการโพสต์ในแกลเลอรีที่ชอบของคุณ" "write:gallery-likes": "แก้ไขรายการโพสต์ในแกลเลอรีที่ชอบของคุณ" _auth: + shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน" shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?" shareAccessAsk: "คุณแน่ใจแล้วจริงๆหรอว่าต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณแน่ใจแล้วหรอ?" + permission: "{name} ได้ขอสิทธิ์การเข้าถึงดังต่อไปนี้" permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้" pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน" callback: "กำลังกลับไปที่แอปพลิเคชัน" denied: "ปฏิเสธการเข้าใช้" + pleaseLogin: "กรุณาเข้าสู่ระบบเพื่ออนุมัติแอปพลิเคชัน" _antennaSources: all: "โน้ตทั้งหมด" homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 9a63bdec4..a10815888 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -129,6 +129,7 @@ unblockConfirm: "确定要解除拉黑吗?" suspendConfirm: "要冻结吗?" unsuspendConfirm: "要解除冻结吗?" selectList: "选择列表" +selectChannel: "选择频道" selectAntenna: "选择天线" selectWidget: "选择小工具" editWidgets: "编辑部件" @@ -256,6 +257,8 @@ noMoreHistory: "没有更多的历史记录" startMessaging: "添加聊天" nUsersRead: "{n}人已读" agreeTo: "勾选则表示已阅读并同意{0}" +agreeBelow: "同意以下观点" +basicNotesBeforeCreateAccount: "基本注意事项" tos: "服务条款" start: "开始" home: "首页" @@ -861,6 +864,8 @@ failedToFetchAccountInformation: "获取账户信息失败" rateLimitExceeded: "已超過速率限制" cropImage: "剪裁图像" cropImageAsk: "是否要裁剪图像?" +cropYes: "已裁剪" +cropNo: "就这样吧!" file: "文件" recentNHours: "最近{n}小时" recentNDays: "最近{n}天" @@ -939,6 +944,8 @@ cannotPerformTemporaryDescription: "因操作过于频繁,暂时不可用, preset: "預設值" selectFromPresets: "從預設值中選擇" achievements: "成就" +gotInvalidResponseError: "服务器无应答" +gotInvalidResponseErrorDescription: "您的网络连接可能出现了问题, 或是远程服务器暂时不可用. 请稍后重试。" _achievements: earnedAt: "达成时间" _types: @@ -1122,7 +1129,7 @@ _achievements: description: "在0点发布一篇帖子" flavor: "嘣 嘣 嘣 Biu——!" _selfQuote: - title: "自我提及" + title: "自我引用" description: "引用了自己的帖子" _htl20npm: title: "流动的时间线" @@ -1593,12 +1600,15 @@ _permissions: "read:gallery-likes": "读取喜欢的图片" "write:gallery-likes": "操作喜欢的图片" _auth: + shareAccessTitle: "应用程序授权许可" shareAccess: "您要授权允许“{name}”访问您的帐户吗?" shareAccessAsk: "您确定要授权此应用程序访问您的帐户吗?" + permission: "{name}需要以下权限" permissionAsk: "这个应用程序需要以下权限" pleaseGoBack: "请返回到应用程序" callback: "回到应用程序" denied: "拒绝访问" + pleaseLogin: "在对应用进行授权许可之前,请先登录" _antennaSources: all: "所有帖子" homeTimeline: "已关注用户的帖子" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 570afce8f..2e1787e7a 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -129,6 +129,7 @@ unblockConfirm: "確定解除封鎖此用戶?" suspendConfirm: "確定凍結此帳號?" unsuspendConfirm: "確定解凍此帳號?" selectList: "選擇清單" +selectChannel: "選擇頻道" selectAntenna: "選擇天線" selectWidget: "選擇小工具" editWidgets: "編輯小工具" @@ -256,6 +257,8 @@ noMoreHistory: "沒有更多歷史紀錄" startMessaging: "開始聊天" nUsersRead: "{n}人已讀" agreeTo: "我同意{0}" +agreeBelow: "同意以下內容" +basicNotesBeforeCreateAccount: "基本注意事項" tos: "使用條款" start: "開始" home: "首頁" @@ -861,6 +864,8 @@ failedToFetchAccountInformation: "取得帳戶資訊失敗" rateLimitExceeded: "已超過速率限制" cropImage: "圖片裁剪" cropImageAsk: "要剪裁圖片嗎?" +cropYes: "裁剪" +cropNo: "使用原圖" file: "檔案" recentNHours: "過去{n}小時" recentNDays: "過去{n}天" @@ -1595,12 +1600,15 @@ _permissions: "read:gallery-likes": "讀取喜歡的圖片" "write:gallery-likes": "操作喜歡的圖片" _auth: + shareAccessTitle: "應用程式的存取權限" shareAccess: "要授權「“{name}”」存取您的帳戶嗎?" shareAccessAsk: "您確定要授權這個應用程式使用您的帳戶嗎?" + permission: "{name}要求以下的權限" permissionAsk: "此應用程式需要以下權限" pleaseGoBack: "請返回至應用程式" callback: "回到應用程式" denied: "拒絕訪問" + pleaseLogin: "必須登入以提供應用程式的存取權限。" _antennaSources: all: "全部貼文" homeTimeline: "來自已追隨使用者的貼文" diff --git a/package.json b/package.json index 578bcb76a..97c9dea7d 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "misskey", - "version": "13.5.5", + "version": "13.5.6", "codename": "nasubi", "repository": { "type": "git", "url": "https://github.com/misskey-dev/misskey.git" }, - "packageManager": "pnpm@7.24.3", + "packageManager": "pnpm@7.27.0", "workspaces": [ "packages/frontend", "packages/backend", diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 4bd6d0f55..49ded6c28 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -255,8 +255,21 @@ export class FileServerService { const isConvertibleImage = isMimeImage(file.mime, 'sharp-convertible-image'); const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image'); + if ( + 'emoji' in request.query || + 'avatar' in request.query || + 'static' in request.query || + 'preview' in request.query || + 'badge' in request.query + ) { + if (!isConvertibleImage) { + // 画像でないなら404でお茶を濁す + throw new StatusError('Unexpected mime', 404); + } + } + let image: IImageStreamable | null = null; - if (('emoji' in request.query || 'avatar' in request.query) && isConvertibleImage) { + if ('emoji' in request.query || 'avatar' in request.query) { if (!isAnimationConvertibleImage && !('static' in request.query)) { image = { data: fs.createReadStream(file.path), @@ -277,16 +290,11 @@ export class FileServerService { type: 'image/webp', }; } - } else if ('static' in request.query && isConvertibleImage) { + } else if ('static' in request.query) { image = this.imageProcessingService.convertToWebpStream(file.path, 498, 280); - } else if ('preview' in request.query && isConvertibleImage) { + } else if ('preview' in request.query) { image = this.imageProcessingService.convertToWebpStream(file.path, 200, 200); } else if ('badge' in request.query) { - if (!isConvertibleImage) { - // 画像でないなら404でお茶を濁す - throw new StatusError('Unexpected mime', 404); - } - const mask = sharp(file.path) .resize(96, 96, { fit: 'inside', diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index 19d04721d..d30037dcf 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -107,6 +107,7 @@ onMounted(() => { } .iconFrame { + position: relative; width: 58px; height: 58px; padding: 6px; diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue index 651b20cef..e0885f555 100644 --- a/packages/frontend/src/components/MkCwButton.vue +++ b/packages/frontend/src/components/MkCwButton.vue @@ -1,5 +1,5 @@ <template> -<button class="_button" :class="$style.root" @click="toggle"> +<button class="_button" :class="$style.root" @mousedown="toggle"> <b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b> <span v-if="!modelValue" :class="$style.label">{{ label }}</span> </button> diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 74cb53485..da4db6340 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -28,8 +28,8 @@ </template> </MkSelect> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> - <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ (showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt }}</MkButton> - <MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.ts.cancel }}</MkButton> + <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> + <MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> </div> <div v-if="actions" :class="$style.buttons"> <MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" @click="() => { action.callback(); close(); }">{{ action.text }}</MkButton> @@ -82,6 +82,8 @@ const props = withDefaults(defineProps<{ showOkButton?: boolean; showCancelButton?: boolean; cancelableByBgClick?: boolean; + okText?: string; + cancelText?: string; }>(), { type: 'info', showOkButton: true, diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue index c4af1ee8d..c0401a645 100644 --- a/packages/frontend/src/components/MkMediaBanner.vue +++ b/packages/frontend/src/components/MkMediaBanner.vue @@ -56,7 +56,7 @@ onMounted(() => { width: 100%; border-radius: 4px; margin-top: 4px; - overflow: clip; + // overflow: clip; --plyr-color-main: var(--accent); --plyr-audio-controls-background: var(--bg); @@ -99,7 +99,7 @@ onMounted(() => { > .audio { border-radius: 8px; - overflow: clip; + // overflow: clip; } } </style> diff --git a/packages/frontend/src/components/MkSignup.vue b/packages/frontend/src/components/MkSignup.vue index c60c566f8..d8703a0b1 100644 --- a/packages/frontend/src/components/MkSignup.vue +++ b/packages/frontend/src/components/MkSignup.vue @@ -50,13 +50,13 @@ <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span> </template> </MkInput> - <MkSwitch v-if="instance.tosUrl" v-model="ToSAgreement" class="tou"> - <I18n :src="i18n.ts.agreeTo"> - <template #0> - <a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a> - </template> - </I18n> + <MkSwitch v-model="ToSAgreement" class="tou"> + <template #label>{{ i18n.ts.agreeBelow }}</template> </MkSwitch> + <ul style="margin: 0; padding-left: 2em;"> + <li v-if="instance.tosUrl"><a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a></li> + <li><a href="https://misskey-hub.net/docs/notes.html" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }}</a></li> + </ul> <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" class="captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 812f30439..9ad06545f 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -1,11 +1,19 @@ <template> -<span v-if="!link" v-user-preview="preview ? user.id : undefined" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" class="_noSelect" :style="{ color }" :title="acct(user)" @click="onClick"> +<span v-if="!link" v-user-preview="preview ? user.id : undefined" class="_noSelect" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick"> <img :class="$style.inner" :src="url" decoding="async"/> <MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> + <template v-if="user.isCat"> + <div :class="$style.earLeft"/> + <div :class="$style.earRight"/> + </template> </span> -<MkA v-else v-user-preview="preview ? user.id : undefined" class="_noSelect" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" :style="{ color }" :to="userPage(user)" :title="acct(user)" :target="target"> +<MkA v-else v-user-preview="preview ? user.id : undefined" class="_noSelect" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" :style="{ color }" :title="acct(user)" :to="userPage(user)" :target="target"> <img :class="$style.inner" :src="url" decoding="async"/> <MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> + <template v-if="user.isCat"> + <div :class="$style.earLeft"/> + <div :class="$style.earRight"/> + </template> </MkA> </template> @@ -110,32 +118,41 @@ watch(() => props.user.avatarBlurhash, () => { } .cat { - &:before, &:after { - background: #df548f; - border: solid 4px currentColor; - box-sizing: border-box; - content: ''; + > .earLeft, + > .earRight { + contain: strict; display: inline-block; height: 50%; width: 50%; + background: currentColor; + + &::before { + contain: strict; + content: ''; + display: block; + width: 60%; + height: 60%; + margin: 20%; + background: #df548f; + } } - &:before { + > .earLeft { border-radius: 0 75% 75%; transform: rotate(37.5deg) skew(30deg); } - &:after { + > .earRight { border-radius: 75% 0 75% 75%; transform: rotate(-37.5deg) skew(-30deg); } &:hover { - &:before { + > .earLeft { animation: earwiggleleft 1s infinite; } - &:after { + > .earRight { animation: earwiggleright 1s infinite; } } diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 52469b6d0..e21a21ef7 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -171,6 +171,8 @@ export function confirm(props: { type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; title?: string | null; text?: string | null; + okText?: string; + cancelText?: string; }): Promise<{ canceled: boolean }> { return new Promise((resolve, reject) => { popup(MkDialog, { diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue index 801295fce..f8484185f 100644 --- a/packages/frontend/src/pages/auth.form.vue +++ b/packages/frontend/src/pages/auth.form.vue @@ -1,60 +1,66 @@ <template> -<section class=""> - <div class="">{{ $t('_auth.shareAccess', { name: app.name }) }}</div> - <div class=""> - <h2>{{ app.name }}</h2> - <p class="id">{{ app.id }}</p> - <p class="description">{{ app.description }}</p> - </div> - <div class=""> - <h2>{{ $ts._auth.permissionAsk }}</h2> - <ul> - <li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li> - </ul> - </div> - <div class=""> - <MkButton inline @click="cancel">{{ $ts.cancel }}</MkButton> - <MkButton inline primary @click="accept">{{ $ts.accept }}</MkButton> - </div> -</section> + <section> + <div v-if="app.permission.length > 0"> + <p>{{ $t('_auth.permission', { name }) }}</p> + <ul> + <li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li> + </ul> + </div> + <div>{{ i18n.t('_auth.shareAccess', { name: `${name} (${app.id})` }) }}</div> + <div :class="$style.buttons"> + <MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton> + <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton> + </div> + </section> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; +import { i18n } from '@/i18n'; +import { AuthSession } from 'misskey-js/built/entities'; -export default defineComponent({ - components: { - MkButton, - }, - props: ['session'], - computed: { - name(): string { - const el = document.createElement('div'); - el.textContent = this.app.name; - return el.innerHTML; - }, - app(): any { - return this.session.app; - }, - }, - methods: { - cancel() { - os.api('auth/deny', { - token: this.session.token, - }).then(() => { - this.$emit('denied'); - }); - }, +const props = defineProps<{ + session: AuthSession; +}>(); - accept() { - os.api('auth/accept', { - token: this.session.token, - }).then(() => { - this.$emit('accepted'); - }); - }, - }, +const emit = defineEmits<{ + (event: 'accepted'): void; + (event: 'denied'): void; +}>(); + +const app = $computed(() => props.session.app); + +const name = $computed(() => { + const el = document.createElement('div'); + el.textContent = app.name; + return el.innerHTML; }); + +function cancel() { + os.api('auth/deny', { + token: props.session.token, + }).then(() => { + emit('denied'); + }); +} + +function accept() { + os.api('auth/accept', { + token: props.session.token, + }).then(() => { + emit('accepted'); + }); +} + </script> + +<style lang="scss" module> +.buttons { + margin-top: 16px; + display: flex; + gap: 8px; + flex-wrap: wrap; +} +</style> diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index b7727ca30..50afffc46 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -1,93 +1,105 @@ <template> -<div v-if="$i && fetching" class=""> - <MkLoading/> -</div> -<div v-else-if="$i"> - <XForm - v-if="state == 'waiting'" - ref="form" - class="form" - :session="session" - @denied="state = 'denied'" - @accepted="accepted" - /> - <div v-if="state == 'denied'" class="denied"> - <h1>{{ $ts._auth.denied }}</h1> - </div> - <div v-if="state == 'accepted'" class="accepted"> - <h1>{{ session.app.isAuthorized ? $t('already-authorized') : $ts.allowed }}</h1> - <p v-if="session.app.callbackUrl">{{ $ts._auth.callback }}<MkEllipsis/></p> - <p v-if="!session.app.callbackUrl">{{ $ts._auth.pleaseGoBack }}</p> - </div> - <div v-if="state == 'fetch-session-error'" class="error"> - <p>{{ $ts.somethingHappened }}</p> - </div> -</div> -<div v-else class="signin"> - <MkSignin @login="onLogin"/> -</div> +<MkStickyContainer> + <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template> + <MkSpacer :content-max="500"> + <div v-if="state == 'fetch-session-error'"> + <p>{{ i18n.ts.somethingHappened }}</p> + </div> + <div v-else-if="$i && !session"> + <MkLoading /> + </div> + <div v-else-if="$i && session"> + <XForm + v-if="state == 'waiting'" + class="form" + :session="session" + @denied="state = 'denied'" + @accepted="accepted" + /> + <div v-if="state == 'denied'"> + <h1>{{ i18n.ts._auth.denied }}</h1> + </div> + <div v-if="state == 'accepted' && session"> + <h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1> + <p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }} + <MkEllipsis /> + </p> + <p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p> + </div> + </div> + <div v-else> + <p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p> + <MkSignin @login="onLogin" /> + </div> + </MkSpacer> +</MkStickyContainer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { onMounted } from 'vue'; import XForm from './auth.form.vue'; import MkSignin from '@/components/MkSignin.vue'; import * as os from '@/os'; -import { login } from '@/account'; +import { $i, login } from '@/account'; +import { definePageMetadata } from '@/scripts/page-metadata'; +import { AuthSession } from 'misskey-js/built/entities'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - XForm, - MkSignin, - }, - props: ['token'], - data() { - return { - state: null, - session: null, - fetching: true, - }; - }, - mounted() { - if (!this.$i) return; +const props = defineProps<{ + token: string; +}>(); - // Fetch session - os.api('auth/session/show', { - token: this.token, - }).then(session => { - this.session = session; - this.fetching = false; +let state = $ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null); +let session = $ref<AuthSession | null>(null); - // 既に連携していた場合 - if (this.session.app.isAuthorized) { - os.api('auth/accept', { - token: this.session.token, - }).then(() => { - this.accepted(); - }); - } else { - this.state = 'waiting'; - } - }).catch(error => { - this.state = 'fetch-session-error'; - this.fetching = false; +function accepted() { + state = 'accepted'; + if (session && session.app.callbackUrl) { + const url = new URL(session.app.callbackUrl); + if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url'); + location.href = `${session.app.callbackUrl}?token=${session.token}`; + } +} + +function onLogin(res) { + login(res.i); +} + +onMounted(async () => { + if (!$i) return; + + try { + session = await os.api('auth/session/show', { + token: props.token, }); - }, - methods: { - accepted() { - this.state = 'accepted'; - if (this.session.app.callbackUrl) { - const url = new URL(this.session.app.callbackUrl); - if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url'); - location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`; - } - }, onLogin(res) { - login(res.i); - }, - }, + + // 既に連携していた場合 + if (session.app.isAuthorized) { + await os.api('auth/accept', { + token: session.token, + }); + accepted(); + } else { + state = 'waiting'; + } + } catch (e) { + state = 'fetch-session-error'; + } +}); + +const headerActions = $computed(() => []); + +const headerTabs = $computed(() => []); + +definePageMetadata({ + title: i18n.ts._auth.shareAccessTitle, + icon: 'ti ti-apps', }); </script> -<style lang="scss" scoped> - +<style lang="scss" module> +.loginMessage { + text-align: center; + margin: 8px 0 24px; +} </style> diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue index 9a4019e5b..915adff27 100644 --- a/packages/frontend/src/pages/miauth.vue +++ b/packages/frontend/src/pages/miauth.vue @@ -1,41 +1,40 @@ <template> -<MkSpacer :content-max="800"> - <div v-if="$i"> - <div v-if="state == 'waiting'" class="waiting"> - <div class=""> +<MkStickyContainer> + <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template> + <MkSpacer :content-max="800"> + <div v-if="$i"> + <div v-if="state == 'waiting'"> <MkLoading/> </div> - </div> - <div v-if="state == 'denied'" class="denied"> - <div class=""> + <div v-if="state == 'denied'"> <p>{{ i18n.ts._auth.denied }}</p> </div> - </div> - <div v-else-if="state == 'accepted'" class="accepted"> - <div class=""> + <div v-else-if="state == 'accepted'" class="accepted"> <p v-if="callback">{{ i18n.ts._auth.callback }}<MkEllipsis/></p> <p v-else>{{ i18n.ts._auth.pleaseGoBack }}</p> </div> - </div> - <div v-else class=""> - <div v-if="name" class="">{{ $t('_auth.shareAccess', { name: name }) }}</div> - <div v-else class="">{{ i18n.ts._auth.shareAccessAsk }}</div> - <div class=""> - <p>{{ i18n.ts._auth.permissionAsk }}</p> - <ul> - <li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li> - </ul> - </div> - <div class=""> - <MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton> - <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton> + <div v-else> + <div v-if="_permissions.length > 0"> + <p v-if="name">{{ $t('_auth.permission', { name }) }}</p> + <p v-else>{{ i18n.ts._auth.permissionAsk }}</p> + <ul> + <li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li> + </ul> + </div> + <div v-if="name">{{ $t('_auth.shareAccess', { name }) }}</div> + <div v-else>{{ i18n.ts._auth.shareAccessAsk }}</div> + <div :class="$style.buttons"> + <MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton> + <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton> + </div> </div> </div> - </div> - <div v-else class="signin"> - <MkSignin @login="onLogin"/> - </div> -</MkSpacer> + <div v-else> + <p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p> + <MkSignin @login="onLogin"/> + </div> + </MkSpacer> +</MkStickyContainer> </template> <script lang="ts" setup> @@ -45,6 +44,7 @@ import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { $i, login } from '@/account'; import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; const props = defineProps<{ session: string; @@ -54,7 +54,7 @@ const props = defineProps<{ permission: string; // コンマ区切り }>(); -const _permissions = props.permission.split(','); +const _permissions = props.permission ? props.permission.split(',') : []; let state = $ref<string | null>(null); @@ -83,8 +83,27 @@ function deny(): void { function onLogin(res): void { login(res.i); } + +const headerActions = $computed(() => []); + +const headerTabs = $computed(() => []); + +definePageMetadata({ + title: 'MiAuth', + icon: 'ti ti-apps', +}); </script> -<style lang="scss" scoped> +<style lang="scss" module> +.buttons { + margin-top: 16px; + display: flex; + gap: 8px; + flex-wrap: wrap; +} +.loginMessage { + text-align: center; + margin: 8px 0 24px; +} </style> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index da7d3d370..3647e90ce 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -150,6 +150,8 @@ function changeAvatar(ev) { const { canceled } = await os.confirm({ type: 'question', text: i18n.t('cropImageAsk'), + okText: i18n.ts.cropYes, + cancelText: i18n.ts.cropNo, }); if (!canceled) { @@ -174,6 +176,8 @@ function changeBanner(ev) { const { canceled } = await os.confirm({ type: 'question', text: i18n.t('cropImageAsk'), + okText: i18n.ts.cropYes, + cancelText: i18n.ts.cropNo, }); if (!canceled) { diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 989d861d2..357db5599 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -35,7 +35,7 @@ <i class="icon ti ti-pencil ti-fw"></i><span class="text">{{ i18n.ts.note }}</span> </button> <button v-click-anime class="item _button account" @click="openAccountMenu"> - <MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> + <MkAvatar :user="$i" class="avatar"/><MkAcct class="text _nowrap" :user="$i"/> </button> </div> </div> @@ -168,20 +168,25 @@ function more() { display: flex; align-items: center; padding-left: 30px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; width: 100%; text-align: left; box-sizing: border-box; margin-top: 16px; > .avatar { + display: block; + flex-shrink: 0; position: relative; width: 32px; aspect-ratio: 1; margin-right: 8px; } + + > .text { + display: block; + flex-shrink: 1; + padding-right: 8px; + } } } diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index e90098397..3c161f679 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -45,7 +45,7 @@ <i class="icon ti ti-pencil ti-fw"></i><span class="text">{{ i18n.ts.note }}</span> </button> <button v-click-anime v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="item _button account" @click="openAccountMenu"> - <MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> + <MkAvatar :user="$i" class="avatar"/><MkAcct class="text _nowrap" :user="$i"/> </button> </div> </div> @@ -217,20 +217,25 @@ function more(ev: MouseEvent) { display: flex; align-items: center; padding-left: 30px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; width: 100%; text-align: left; box-sizing: border-box; margin-top: 16px; > .avatar { + display: block; + flex-shrink: 0; position: relative; width: 32px; aspect-ratio: 1; margin-right: 8px; } + + > .text { + display: block; + flex-shrink: 1; + padding-right: 8px; + } } }