From bb330533c1873894fe6d7e48a507b3bd7aac745d Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 26 Jan 2024 14:24:38 +0900 Subject: [PATCH 01/36] New Crowdin updates (#13082) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (German) * New translations ja-jp.yml (English) * New translations ja-jp.yml (German) * New translations ja-jp.yml (English) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Japanese, Kansai) --- locales/de-DE.yml | 9 ++++- locales/en-US.yml | 89 ++++++++++++++++++++++++++++++++++++++++++++++- locales/ja-KS.yml | 6 ++-- locales/zh-CN.yml | 4 +++ 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 45e20d8541..7c05186b99 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -181,7 +181,7 @@ searchWith: "Suchen: {q}" youHaveNoLists: "Du hast keine Listen" followConfirm: "Möchtest du {name} wirklich folgen?" proxyAccount: "Proxy-Benutzerkonto" -proxyAccountDescription: "Ein Proxy-Benutzerkonto ist ein Benutzerkonto, das sich für Nutzer unter bestimmten Konditionen wie ein Follower aus einer fremden Instanz verhält. Zum Beispiel wird die Aktivität eines Nutzers aus einer fremden Instanz nicht an diese Instanz übermittelt, falls es keinen Benutzer dieser Instanz gibt, der diesem Nutzer aus fremder Instanz folgt. In diesem Fall folgt stattdessen das Proxy-Benutzerkonto." +proxyAccountDescription: "Ein Proxy-Konto ist ein Benutzerkonto, das unter bestimmten Bedingungen als Follower für Benutzer fremder Instanzen fungiert. Wenn zum Beispiel ein Benutzer einen Benutzer einer fremden Instanz zu einer Liste hinzufügt, werden die Aktivitäten des entfernten Benutzers nicht an die Instanz übermittelt, wenn kein lokaler Benutzer diesem Benutzer folgt; stattdessen folgt das Proxy-Konto." host: "Hostname" selectUser: "Benutzer auswählen" recipient: "Empfänger" @@ -1181,6 +1181,7 @@ _announcement: tooManyActiveAnnouncementDescription: "Zu viele aktive Ankündigungen können die Benutzerfreundlichkeit verschlechtern. Es wird empfohlen, veraltete Ankündigungen zu archivieren." readConfirmTitle: "Als gelesen markieren?" readConfirmText: "Dies markiert den Inhalt von \"{title}\" als gelesen." + shouldNotBeUsedToPresentPermanentInfo: "Es wird empfohlen, Ankündigungen für aktuelle und zeitlich begrenzte Neuigkeiten zu nutzen, statt für Informationen, die langfristig relevant sind." dialogAnnouncementUxWarn: "Bei der Verwendung von mehr als zwei Meldungen im Dialog-Format wird um Vorsicht geboten, da dies negative Auswirkungen auf die UX haben kann." silence: "Keine Benachrichtigung" silenceDescription: "Wenn aktiviert, gibt diese Meldung keine Nachricht aus und muss nicht als \"gelesen\" markiert werden." @@ -1222,6 +1223,7 @@ _serverSettings: shortName: "Abkürzung" shortNameDescription: "Ein Kürzel für den Namen der Instanz, der angezeigt werden kann, falls der volle Instanzname lang ist." fanoutTimelineDescription: "Ist diese Option aktiviert, kann eine erhebliche Verbesserung im Abrufen von Chroniken und eine Reduzierung der Datenbankbelastung erzielt werden, im Gegenzug zu einer Steigerung in der Speichernutzung von Redis. Bei geringem Serverspeicher oder Serverinstabilität kann diese Option deaktiviert werden." + fanoutTimelineDbFallback: "Auf die Datenbank zurückfallen" _accountMigration: moveFrom: "Von einem anderen Konto zu diesem migrieren" moveFromSub: "Alias für ein anderes Konto erstellen" @@ -1489,7 +1491,9 @@ _role: assignTarget: "Zuweisungsart" descriptionOfAssignTarget: "Manuell bedeutet, dass die Liste der Benutzer einer Rolle manuell verwaltet wird.\nKonditional bedeutet, dass die Liste der Benutzer einer Rolle durch eine Bedingung automatisch verwaltet wird." manual: "Manuell" + manualRoles: "Manuelle Rollen" conditional: "Konditional" + conditionalRoles: "Bedingte Rolle" condition: "Bedingung" isConditionalRole: "Dies ist eine konditionale Rolle." isPublic: "Öffentliche Rolle" @@ -1538,6 +1542,7 @@ _role: canHideAds: "Kann Werbung ausblenden" canSearchNotes: "Nutzung der Notizsuchfunktion" canUseTranslator: "Verwendung des Übersetzers" + avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können" _condition: isLocal: "Lokaler Benutzer" isRemote: "Benutzer fremder Instanz" @@ -1566,6 +1571,7 @@ _emailUnavailable: disposable: "Wegwerf-Email-Adressen können nicht verwendet werden" mx: "Dieser Email-Server ist ungültig" smtp: "Dieser Email-Server antwortet nicht" + banned: "Du kannst dich mit dieser E-Mail-Adresse nicht registrieren" _ffVisibility: public: "Öffentlich" followers: "Nur für Follower sichtbar" @@ -1894,6 +1900,7 @@ _widgets: _userList: chooseList: "Liste auswählen" clicker: "Klickzähler" + birthdayFollowings: "Nutzer, die heute Geburtstag haben" _cw: hide: "Inhalt verbergen" show: "Inhalt anzeigen" diff --git a/locales/en-US.yml b/locales/en-US.yml index fb4cb0a346..6e95040f66 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -122,7 +122,11 @@ add: "Add" reaction: "Reactions" reactions: "Reactions" emojiPicker: "Emoji picker" +pinnedEmojisForReactionSettingDescription: "Set the emojis which should be pinned and displayed immediately when reacting." +pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker" emojiPickerDisplay: "Emoji picker display" +overwriteFromPinnedEmojisForReaction: "Override from reaction settings" +overwriteFromPinnedEmojis: "Override from general settings" reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add." rememberNoteVisibility: "Remember note visibility settings" attachCancel: "Remove attachment" @@ -376,8 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Enable hCaptcha" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptcha: "mCaptcha" +enableMcaptcha: "Enable mCaptcha" mcaptchaSiteKey: "Site key" mcaptchaSecretKey: "Secret key" +mcaptchaInstanceUrl: "mCaptcha instance URL" recaptcha: "reCAPTCHA" enableRecaptcha: "Enable reCAPTCHA" recaptchaSiteKey: "Site key" @@ -625,6 +632,7 @@ medium: "Medium" small: "Small" generateAccessToken: "Generate access token" permission: "Permissions" +adminPermission: "Admin Permissions" enableAll: "Enable all" disableAll: "Disable all" tokenRequested: "Grant access to account" @@ -668,6 +676,7 @@ useGlobalSettingDesc: "If turned on, your account's notification settings will b other: "Other" regenerateLoginToken: "Regenerate login token" regenerateLoginTokenDescription: "Regenerates the token used internally during login. Normally this action is not necessary. If regenerated, all devices will be logged out." +theKeywordWhenSearchingForCustomEmoji: "This is the keyword when searching for custom emojis." setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces." fileIdOrUrl: "File ID or URL" behavior: "Behavior" @@ -1050,6 +1059,8 @@ limitWidthOfReaction: "Limits the maximum width of reactions and display them in noteIdOrUrl: "Note ID or URL" video: "Video" videos: "Videos" +audio: "Audio" +audioFiles: "Audio" dataSaver: "Data Saver" accountMigration: "Account Migration" accountMoved: "This user has moved to a new account:" @@ -1162,6 +1173,7 @@ tosAndPrivacyPolicy: "Terms of Service and Privacy Policy" avatarDecorations: "Avatar decorations" attach: "Attach" detach: "Remove" +detachAll: "Remove All" angle: "Angle" flip: "Flip" showAvatarDecorations: "Show avatar decorations" @@ -1175,11 +1187,31 @@ cwNotationRequired: "If \"Hide content\" is enabled, a description must be provi doReaction: "Add reaction" code: "Code" reloadRequiredToApplySettings: "Reloading is required to apply the settings." +remainingN: "Remaining: {n}" +overwriteContentConfirm: "Are you sure you want to overwrite the current content?" +seasonalScreenEffect: "Seasonal Screen Effect" decorate: "Decorate" +addMfmFunction: "Add MFM" +enableQuickAddMfmFunction: "Show advanced MFM picker" bubbleGame: "Bubble Game" sfx: "Sound Effects" +soundWillBePlayed: "Sound will be played" +showReplay: "View Replay" replay: "Replay" +replaying: "Showing replay" +ranking: "Ranking" lastNDays: "Last {n} days" +backToTitle: "Go back to title" +hemisphere: "Where are you located" +withSensitive: "Include notes with sensitive files" +userSaysSomethingSensitive: "Post by {name} contains sensitive content" +enableHorizontalSwipe: "Swipe to switch tabs" +_bubbleGame: + howToPlay: "How to play" + _howToPlay: + section1: "Adjust the position and drop the object into the box." + section2: "When two objects of the same type touch each other, they will change into a different object and you score points." + section3: "The game is over when objects overflow from the box. Aim for a high score by fusing objects together while you avoid overflowing the box!" _announcement: forExistingUsers: "Existing users only" forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it." @@ -1189,7 +1221,7 @@ _announcement: tooManyActiveAnnouncementDescription: "Having too many active announcements may worsen the user experience. Please consider archiving announcements that have become obsolete." readConfirmTitle: "Mark as read?" readConfirmText: "This will mark the contents of \"{title}\" as read." - shouldNotBeUsedToPresentPermanentInfo: "As it may significantly impact the user experience for new users, it is recommended to use notifications in the flow information rather than stock information." + shouldNotBeUsedToPresentPermanentInfo: "It's best to use announcements to publish fresh and time-bound information, not for information that will be relevant in the long term." dialogAnnouncementUxWarn: "Having two or more dialog-style notifications simultaneously can significantly impact the user experience, so please use them carefully." silence: "No notification" silenceDescription: "Turning this on will skip the notification of this announcement and the user won't need to read it." @@ -1552,8 +1584,11 @@ _achievements: description: "Tutorial completed" _bubbleGameExplodingHead: title: "🤯" + description: "The biggest object in the bubble game" _bubbleGameDoubleExplodingHead: title: "Double🤯" + description: "Two of the biggest objects in the bubble game at the same time" + flavor: "You can fill a lunch box like this 🤯 🤯 a bit." _role: new: "New role" edit: "Edit role" @@ -1615,6 +1650,7 @@ _role: canHideAds: "Can hide ads" canSearchNotes: "Usage of note search" canUseTranslator: "Translator usage" + avatarDecorationLimit: "Maximum number of avatar decorations that can be applied" _condition: isLocal: "Local user" isRemote: "Remote user" @@ -1643,6 +1679,7 @@ _emailUnavailable: disposable: "Disposable email addresses may not be used" mx: "This email server is invalid" smtp: "This email server is not responding" + banned: "You cannot register with this email address" _ffVisibility: public: "Public" followers: "Visible to followers only" @@ -2051,6 +2088,7 @@ _profile: changeAvatar: "Change avatar" changeBanner: "Change banner" verifiedLinkDescription: "By entering an URL that contains a link to your profile here, an ownership verification icon can be displayed next to the field." + avatarDecorationMax: "You can add up to {max} decorations." _exportOrImport: allNotes: "All notes" favoritedNotes: "Favorite notes" @@ -2344,14 +2382,63 @@ _externalResourceInstaller: _dataSaver: _media: title: "Loading Media" + description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped." _avatar: title: "Avatar image" description: "Stop avatar image animation. Animated images can be larger in file size than normal images, potentially leading to further reductions in data traffic." _urlPreview: title: "URL preview thumbnails" + description: "URL preview thumbnail images will no longer be loaded." _code: title: "Code highlighting" description: "If code highlighting notations are used in MFM, etc., they will not load until tapped. Syntax highlighting requires downloading the highlight definition files for each programming language. Therefore, disabling the automatic loading of these files is expected to reduce the amount of communication data." +_hemisphere: + N: "Northern Hemisphere" + S: "Southern Hemisphere" + caption: "Used in some client settings to determine season." _reversi: + reversi: "Reversi" + gameSettings: "Game settings" + chooseBoard: "Choose a board" + blackOrWhite: "Black/White" + blackIs: "{name} is playing Black" + rules: "Rules" + thisGameIsStartedSoon: "The game will begin shortly" + waitingForOther: "Waiting for opponent's turn" + waitingForMe: "Waiting for your turn" + waitingBoth: "Get ready" + ready: "Ready" + cancelReady: "Not ready" + opponentTurn: "Opponent's turn" + myTurn: "Your turn" + turnOf: "It's {name}'s turn" + pastTurnOf: "{name}'s turn" + surrender: "Surrender" + surrendered: "Surrendered" + timeout: "Out of time" + drawn: "Draw" + won: "{name} wins" + black: "Black" + white: "White" total: "Total" + turnCount: "Turn {count}" + myGames: "My rounds" + allGames: "All rounds" + ended: "Ended" + playing: "Currently playing" + isLlotheo: "The one with fewer stones wins (Llotheo)" + loopedMap: "Looping map" + canPutEverywhere: "Tiles are placeable everywhere" + timeLimitForEachTurn: "Time limit for turn" + freeMatch: "Free Match" + lookingForPlayer: "Finding opponent..." + gameCanceled: "The game has been cancelled." + shareToTlTheGameWhenStart: "Share Game to timeline when started" + iStartedAGame: "The game has begun! #MisskeyReversi" + opponentHasSettingsChanged: "The opponent has changed their settings." + allowIrregularRules: "Irregular rules (completely free)" + disallowIrregularRules: "No irregular rules" +_offlineScreen: + title: "Offline - cannot connect to the server" + header: "Unable to connect to the server" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 719812bfdb..4a3a79743d 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -2033,9 +2033,9 @@ _auth: _antennaSources: all: "みんなのノート" homeTimeline: "フォローしとるユーザーのノート" - users: "選らんだ一人か複数のユーザーのノート" + users: "選んだ一人か複数のユーザーのノート" userList: "選んだリストのユーザーのノート" - userBlacklist: "選んだ1人か複数のユーザーのノート" + userBlacklist: "選んだ一人か複数のユーザーを除いた全てのノート" _weekday: sunday: "日曜日" monday: "月曜日" @@ -2485,6 +2485,8 @@ _reversi: shareToTlTheGameWhenStart: "初めの時に対局をタイムラインに投稿するで" iStartedAGame: "対局し始めたで! #MisskeyReversi" opponentHasSettingsChanged: "相手が設定変えたで" + allowIrregularRules: "変則許可 (完全フリー)" + disallowIrregularRules: "変則なし" _offlineScreen: title: "オフライン - サーバーに接続できひんで" header: "サーバーに接続できへんわ" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index d14a77f5aa..a7a6f200f5 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1185,6 +1185,7 @@ useGroupedNotifications: "分组显示通知" signupPendingError: "确认电子邮件时出现错误。链接可能已过期。" cwNotationRequired: "在启用「隐藏内容」时必须输入注释" doReaction: "回应" +code: "代码" reloadRequiredToApplySettings: "需要重新载入来使设置生效" remainingN: "剩余:{n}" overwriteContentConfirm: "将覆盖现有内容。确定吗?" @@ -1202,6 +1203,7 @@ lastNDays: "最近 {n} 天" backToTitle: "返回标题" hemisphere: "居住地区" withSensitive: "显示包含敏感媒体的帖子" +userSaysSomethingSensitive: "含 {name} 敏感文件的帖子" enableHorizontalSwipe: "滑动切换标签页" _bubbleGame: howToPlay: "游戏说明" @@ -2435,6 +2437,8 @@ _hemisphere: caption: "在某些客户端设置中用来确定季节" _reversi: reversi: "黑白棋" + rules: "规则" + ready: "准备就绪" total: "总计" _offlineScreen: title: "离线——无法连接到服务器" From b7270c6238595b4a1cbc38a57e5e2abe69af7f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Sat, 27 Jan 2024 09:18:09 +0900 Subject: [PATCH 02/36] =?UTF-8?q?fix(dev):=20pnpm=20dev=E3=81=A7=E4=BE=9D?= =?UTF-8?q?=E5=AD=98=E9=96=A2=E4=BF=82=E6=9B=B4=E6=96=B0=E3=81=8C=E4=B8=80?= =?UTF-8?q?=E9=83=A8=E5=8F=8D=E6=98=A0=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=20(#13091)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/misskey-bubble-game/package.json | 3 ++- packages/misskey-js/package.json | 2 +- packages/misskey-reversi/package.json | 1 + scripts/dev.mjs | 12 +++++++++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json index 5a6b952e07..9de7ba005f 100644 --- a/packages/misskey-bubble-game/package.json +++ b/packages/misskey-bubble-game/package.json @@ -2,6 +2,7 @@ "type": "module", "name": "misskey-bubble-game", "version": "0.0.1", + "types": "./built/dts/index.d.ts", "exports": { ".": { "import": "./built/esm/index.js", @@ -15,7 +16,7 @@ "scripts": { "build": "node ./build.js", "build:tsc": "npm run tsc", - "tsc": "npm run ts-esm && npm run ts-dts", + "tsc": "npm run tsc-esm && npm run tsc-dts", "tsc-esm": "tsc --outDir built/esm", "tsc-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true", "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build:tsc\"", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 06c3ce6b54..661f9e53f2 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -19,7 +19,7 @@ "ts": "npm run ts-esm && npm run ts-dts", "ts-esm": "tsc --outDir built/esm", "ts-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true", - "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build\"", + "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run ts\"", "tsd": "tsd", "api": "pnpm api-extractor run --local --verbose", "api-prod": "pnpm api-extractor run --verbose", diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json index bd8d4b498c..52d497d3f8 100644 --- a/packages/misskey-reversi/package.json +++ b/packages/misskey-reversi/package.json @@ -2,6 +2,7 @@ "type": "module", "name": "misskey-reversi", "version": "0.0.1", + "types": "./built/dts/index.d.ts", "exports": { ".": { "import": "./built/esm/index.js", diff --git a/scripts/dev.mjs b/scripts/dev.mjs index 9b60af6570..5a250b1b2c 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -28,19 +28,19 @@ await execa('pnpm', ['build-assets'], { stderr: process.stderr, }); -await execa('pnpm', ['--filter', 'misskey-js', 'build'], { +await execa('pnpm', ['--filter', 'misskey-js', 'ts'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, }); -await execa('pnpm', ['--filter', 'misskey-reversi', 'build'], { +await execa('pnpm', ['--filter', 'misskey-reversi', 'build:tsc'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, }); -await execa('pnpm', ['--filter', 'misskey-bubble-game', 'build'], { +await execa('pnpm', ['--filter', 'misskey-bubble-game', 'build:tsc'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, @@ -76,6 +76,12 @@ execa('pnpm', ['--filter', 'sw', 'watch'], { stderr: process.stderr, }); +execa('pnpm', ['--filter', 'misskey-js', 'watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); + execa('pnpm', ['--filter', 'misskey-reversi', 'watch'], { cwd: _dirname + '/../', stdout: process.stdout, From 15727088be97b18cf1adabc7b4ee11565a5ae692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sat, 27 Jan 2024 10:34:07 +0900 Subject: [PATCH 03/36] fix misskey-js version --- packages/misskey-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 661f9e53f2..ad9afd6742 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2024.2.0-beta.3", + "version": "2024.2.0-beta.7", "description": "Misskey SDK for JavaScript", "types": "./built/dts/index.d.ts", "exports": { From 30f4023c36aabc1d1542e0dda1b86d0a3844f250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sat, 27 Jan 2024 16:33:30 +0900 Subject: [PATCH 04/36] =?UTF-8?q?refactor(frontend/MediaPlayer):=20css?= =?UTF-8?q?=E3=81=AE=E9=87=8D=E8=A4=87=E3=82=92=E5=89=8A=E9=99=A4=20(#1309?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update MkMediaAudio.vue * Update MkMediaVideo.vue --- packages/frontend/src/components/MkMediaAudio.vue | 3 +-- packages/frontend/src/components/MkMediaVideo.vue | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 3c569e2e1e..cf44b70381 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -273,7 +273,7 @@ onDeactivated(() => { .hidden { width: 100%; - background: none; + background: #000; border: none; outline: none; font: inherit; @@ -283,7 +283,6 @@ onDeactivated(() => { display: flex; align-items: center; justify-content: center; - background: #000; } .hiddenTextWrapper { diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index 0a113458a1..e1ad5f0ce3 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -396,7 +396,7 @@ onDeactivated(() => { .hidden { width: 100%; - background: none; + background: #000; border: none; outline: none; font: inherit; @@ -406,7 +406,6 @@ onDeactivated(() => { display: flex; align-items: center; justify-content: center; - background: #000; } .hiddenTextWrapper { @@ -466,7 +465,6 @@ onDeactivated(() => { grid-template-columns: auto auto 1fr auto auto; align-items: center; gap: 4px 8px; - pointer-events: none; padding: 35px 10px 10px 10px; background: linear-gradient(rgba(0, 0, 0, 0),rgba(0, 0, 0, .75)); From 9753cce4aa602957c08c253dd728d0d43318eb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sat, 27 Jan 2024 18:25:15 +0900 Subject: [PATCH 05/36] =?UTF-8?q?enhance(frontend):=20=E3=83=AA=E3=83=A2?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AE=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?= =?UTF-8?q?=E3=81=AF=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=8B=E3=82=89?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88=E3=81=A7?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=20(#13087)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(frontend): リモートのユーザーはメニューから直接リモートで表示できるように * change changelog * Apply suggestions from code review Co-authored-by: syuilo --------- Co-authored-by: syuilo --- CHANGELOG.md | 1 + packages/frontend/src/scripts/get-user-menu.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cff0fc07cd..7edc9cf7cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - Enhance: ノート作成画面のファイル添付メニューから直接ファイルを削除できるように - Enhance: MFMの属性でオートコンプリートが使用できるように #12735 - Enhance: 絵文字編集ダイアログをモーダルではなくウィンドウで表示するように +- Enhance: リモートのユーザーはメニューから直接リモートで表示できるように - Fix: ネイティブモードの絵文字がモノクロにならないように - Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正 - Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正 diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index d9a52c3741..e2bd3d3a93 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -170,7 +170,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter action: () => { copyToClipboard(`${user.host ?? host}/@${user.username}.atom`); }, - }, { + }, ...(user.host != null && user.url != null ? [{ + icon: 'ti ti-external-link', + text: i18n.ts.showOnRemote, + action: () => { + if (user.url == null) return; + window.open(user.url, '_blank', 'noopener'); + }, + }] : []), { icon: 'ti ti-share', text: i18n.ts.copyProfileUrl, action: () => { From cdac3988b546f7cf457767f30ef9e24a591ae9d7 Mon Sep 17 00:00:00 2001 From: woxtu Date: Sun, 28 Jan 2024 15:08:45 +0900 Subject: [PATCH 06/36] fix(backend): Fix typos in job configurations (#13086) * Fix typos * Update CHANGELOG --- .config/example.yml | 6 +++--- CHANGELOG.md | 1 + packages/backend/src/config.ts | 12 ++++++------ packages/backend/src/queue/QueueProcessorService.ts | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index df423c2c83..3c9c3bc0d7 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -160,14 +160,14 @@ id: 'aidx' # Job concurrency per worker #deliverJobConcurrency: 128 #inboxJobConcurrency: 16 -#relashionshipJobConcurrency: 16 -# What's relashionshipJob?: +#relationshipJobConcurrency: 16 +# What's relationshipJob?: # Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations. # Job rate limiter #deliverJobPerSec: 128 #inboxJobPerSec: 32 -#relashionshipJobPerSec: 64 +#relationshipJobPerSec: 64 # Job attempts #deliverJobMaxAttempts: 12 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edc9cf7cb..98d99b7af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ - Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 - Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正 - Fix: properly handle cc followers +- Fix: ジョブに関する設定の名前を修正 ### Service Worker - Enhance: オフライン表示のデザインを改善・多言語対応 diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index b25554b229..d433ce0eec 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -74,10 +74,10 @@ type Source = { deliverJobConcurrency?: number; inboxJobConcurrency?: number; - relashionshipJobConcurrency?: number; + relationshipJobConcurrency?: number; deliverJobPerSec?: number; inboxJobPerSec?: number; - relashionshipJobPerSec?: number; + relationshipJobPerSec?: number; deliverJobMaxAttempts?: number; inboxJobMaxAttempts?: number; @@ -135,10 +135,10 @@ export type Config = { outgoingAddressFamily: 'ipv4' | 'ipv6' | 'dual' | undefined; deliverJobConcurrency: number | undefined; inboxJobConcurrency: number | undefined; - relashionshipJobConcurrency: number | undefined; + relationshipJobConcurrency: number | undefined; deliverJobPerSec: number | undefined; inboxJobPerSec: number | undefined; - relashionshipJobPerSec: number | undefined; + relationshipJobPerSec: number | undefined; deliverJobMaxAttempts: number | undefined; inboxJobMaxAttempts: number | undefined; proxyRemoteFiles: boolean | undefined; @@ -241,10 +241,10 @@ export function loadConfig(): Config { outgoingAddressFamily: config.outgoingAddressFamily, deliverJobConcurrency: config.deliverJobConcurrency, inboxJobConcurrency: config.inboxJobConcurrency, - relashionshipJobConcurrency: config.relashionshipJobConcurrency, + relationshipJobConcurrency: config.relationshipJobConcurrency, deliverJobPerSec: config.deliverJobPerSec, inboxJobPerSec: config.inboxJobPerSec, - relashionshipJobPerSec: config.relashionshipJobPerSec, + relationshipJobPerSec: config.relationshipJobPerSec, deliverJobMaxAttempts: config.deliverJobMaxAttempts, inboxJobMaxAttempts: config.inboxJobMaxAttempts, proxyRemoteFiles: config.proxyRemoteFiles, diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index bcc1a69f80..cc64c9f5a3 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -283,9 +283,9 @@ export class QueueProcessorService implements OnApplicationShutdown { }, { ...baseQueueOptions(this.config, QUEUE.RELATIONSHIP), autorun: false, - concurrency: this.config.relashionshipJobConcurrency ?? 16, + concurrency: this.config.relationshipJobConcurrency ?? 16, limiter: { - max: this.config.relashionshipJobPerSec ?? 64, + max: this.config.relationshipJobPerSec ?? 64, duration: 1000, }, }); From fe7036a1a875d507d5db8446617df1681e13d915 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 28 Jan 2024 15:09:32 +0900 Subject: [PATCH 07/36] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98d99b7af0..345bd92c38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,7 +63,7 @@ - Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 - Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正 - Fix: properly handle cc followers -- Fix: ジョブに関する設定の名前を修正 +- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec ### Service Worker - Enhance: オフライン表示のデザインを改善・多言語対応 From b62d9f3920d94f68a6e0339c7d73659bbf5a0150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:22:38 +0900 Subject: [PATCH 08/36] =?UTF-8?q?feat(frontend/nirax):=20=E3=83=AA?= =?UTF-8?q?=E3=83=80=E3=82=A4=E3=83=AC=E3=82=AF=E3=83=88=E3=82=92=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=20(#13030)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(frontend/nirax): リダイレクトを設定できるように * revert demonstrative changes * fix * revert unrelated changes * リダイレクトの際にパスが変わらない問題を修正 * リダイレクトが必要なrouteを設定 * fix lint * router向けe2eテストの追加 * fix --------- Co-authored-by: syuilo Co-authored-by: samunohito <46447427+samunohito@users.noreply.github.com> --- cypress/e2e/router.cy.js | 30 ++++++ .../frontend/src/components/MkPageWindow.vue | 7 ++ .../frontend/src/global/router/definition.ts | 19 +++- packages/frontend/src/nirax.ts | 91 ++++++++++++++++--- 4 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 cypress/e2e/router.cy.js diff --git a/cypress/e2e/router.cy.js b/cypress/e2e/router.cy.js new file mode 100644 index 0000000000..81f497b5b8 --- /dev/null +++ b/cypress/e2e/router.cy.js @@ -0,0 +1,30 @@ +describe('Router transition', () => { + describe('Redirect', () => { + // サーバの初期化。ルートのテストに関しては各describeごとに1度だけ実行で十分だと思う(使いまわした方が早い) + before(() => { + cy.resetState(); + + // インスタンス初期セットアップ + cy.registerUser('admin', 'pass', true); + + // ユーザー作成 + cy.registerUser('alice', 'alice1234'); + + cy.login('alice', 'alice1234'); + + // アカウント初期設定ウィザード + // 表示に時間がかかるのでデフォルト秒数だとタイムアウトする + cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 12000 }).click(); + cy.wait(500); + cy.get('[data-cy-modal-dialog-ok]').click(); + }); + + it('redirect to user profile', () => { + // テストのためだけに用意されたリダイレクト用ルートに飛ぶ + cy.visit('/redirect-test'); + + // プロフィールページのURLであることを確認する + cy.url().should('include', '/@alice') + }); + }); +}); diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 28058c338b..ccd9df83ed 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -93,6 +93,13 @@ windowRouter.addListener('push', ctx => { history.value.push({ path: ctx.path, key: ctx.key }); }); +windowRouter.addListener('replace', ctx => { + history.value.pop(); + history.value.push({ path: ctx.path, key: ctx.key }); +}); + +windowRouter.init(); + provide('router', windowRouter); provideMetadataReceiver((info) => { pageMetadata.value = info; diff --git a/packages/frontend/src/global/router/definition.ts b/packages/frontend/src/global/router/definition.ts index 0333770a64..241b4fbcc7 100644 --- a/packages/frontend/src/global/router/definition.ts +++ b/packages/frontend/src/global/router/definition.ts @@ -4,6 +4,7 @@ */ import { App, AsyncComponentLoader, defineAsyncComponent, provide } from 'vue'; +import type { RouteDef } from '@/nirax.js'; import { IRouter, Router } from '@/nirax.js'; import { $i, iAmModerator } from '@/account.js'; import MkLoading from '@/pages/_loading_.vue'; @@ -16,7 +17,7 @@ const page = (loader: AsyncComponentLoader) => defineAsyncComponent({ errorComponent: MkError, }); -const routes = [{ +const routes: RouteDef[] = [{ path: '/@:initUser/pages/:initPageName/view-source', component: page(() => import('@/pages/page-editor/page-editor.vue')), }, { @@ -333,8 +334,7 @@ const routes = [{ component: page(() => import('@/pages/registry.vue')), }, { path: '/install-extentions', - // Note: This path is kept for compatibility. It may be deleted. - component: page(() => import('@/pages/install-extensions.vue')), + redirect: '/install-extensions', loginRequired: true, }, { path: '/install-extensions', @@ -557,6 +557,11 @@ const routes = [{ path: '/', component: $i ? page(() => import('@/pages/timeline.vue')) : page(() => import('@/pages/welcome.vue')), globalCacheKey: 'index', +}, { + // テスト用リダイレクト設定。ログイン中ユーザのプロフィールにリダイレクトする + path: '/redirect-test', + redirect: $i ? `@${$i.username}` : '/', + loginRequired: true, }, { path: '/:(*)', component: page(() => import('@/pages/not-found.vue')), @@ -575,8 +580,6 @@ export function setupRouter(app: App) { const mainRouter = createRouterImpl(location.pathname + location.search + location.hash); - window.history.replaceState({ key: mainRouter.getCurrentKey() }, '', location.href); - window.addEventListener('popstate', (event) => { mainRouter.replace(location.pathname + location.search + location.hash, event.state?.key); }); @@ -585,5 +588,11 @@ export function setupRouter(app: App) { window.history.pushState({ key: ctx.key }, '', ctx.path); }); + mainRouter.addListener('replace', ctx => { + window.history.replaceState({ key: ctx.key }, '', ctx.path); + }); + + mainRouter.init(); + setMainRouter(mainRouter); } diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index a56aa6419e..ddb2a085db 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -9,16 +9,25 @@ import { Component, onMounted, shallowRef, ShallowRef } from 'vue'; import { EventEmitter } from 'eventemitter3'; import { safeURIDecode } from '@/scripts/safe-uri-decode.js'; -export type RouteDef = { +interface RouteDefBase { path: string; - component: Component; query?: Record; loginRequired?: boolean; name?: string; hash?: string; globalCacheKey?: string; children?: RouteDef[]; -}; +} + +interface RouteDefWithComponent extends RouteDefBase { + component: Component, +} + +interface RouteDefWithRedirect extends RouteDefBase { + redirect: string | ((props: Map) => string); +} + +export type RouteDef = RouteDefWithComponent | RouteDefWithRedirect; type ParsedPath = (string | { name: string; @@ -48,7 +57,19 @@ export type RouterEvent = { same: () => void; } -export type Resolved = { route: RouteDef; props: Map; child?: Resolved; }; +export type Resolved = { + route: RouteDef; + props: Map; + child?: Resolved; + redirected?: boolean; + + /** @internal */ + _parsedRoute: { + fullPath: string; + queryString: string | null; + hash: string | null; + }; +}; function parsePath(path: string): ParsedPath { const res = [] as ParsedPath; @@ -81,6 +102,11 @@ export interface IRouter extends EventEmitter { currentRoute: ShallowRef; navHook: ((path: string, flag?: any) => boolean) | null; + /** + * ルートの初期化(eventListenerの定義後に必ず呼び出すこと) + */ + init(): void; + resolve(path: string): Resolved | null; getCurrentPath(): any; @@ -156,12 +182,13 @@ export interface IRouter extends EventEmitter { export class Router extends EventEmitter implements IRouter { private routes: RouteDef[]; public current: Resolved; - public currentRef: ShallowRef = shallowRef(); - public currentRoute: ShallowRef = shallowRef(); + public currentRef: ShallowRef; + public currentRoute: ShallowRef; private currentPath: string; private isLoggedIn: boolean; private notFoundPageComponent: Component; private currentKey = Date.now().toString(); + private redirectCount = 0; public navHook: ((path: string, flag?: any) => boolean) | null = null; @@ -169,13 +196,24 @@ export class Router extends EventEmitter implements IRouter { super(); this.routes = routes; + this.current = this.resolve(currentPath)!; + this.currentRef = shallowRef(this.current); + this.currentRoute = shallowRef(this.current.route); this.currentPath = currentPath; this.isLoggedIn = isLoggedIn; this.notFoundPageComponent = notFoundPageComponent; - this.navigate(currentPath, null, false); + } + + public init() { + const res = this.navigate(this.currentPath, null, false); + this.emit('replace', { + path: res._parsedRoute.fullPath, + key: this.currentKey, + }); } public resolve(path: string): Resolved | null { + const fullPath = path; let queryString: string | null = null; let hash: string | null = null; if (path[0] === '/') path = path.substring(1); @@ -188,6 +226,12 @@ export class Router extends EventEmitter implements IRouter { path = path.substring(0, path.indexOf('?')); } + const _parsedRoute = { + fullPath, + queryString, + hash, + }; + if (_DEV_) console.log('Routing: ', path, queryString); function check(routes: RouteDef[], _parts: string[]): Resolved | null { @@ -238,6 +282,7 @@ export class Router extends EventEmitter implements IRouter { route, props, child, + _parsedRoute, }; } else { continue forEachRouteLoop; @@ -263,6 +308,7 @@ export class Router extends EventEmitter implements IRouter { return { route, props, + _parsedRoute, }; } else { if (route.children) { @@ -272,6 +318,7 @@ export class Router extends EventEmitter implements IRouter { route, props, child, + _parsedRoute, }; } else { continue forEachRouteLoop; @@ -290,7 +337,7 @@ export class Router extends EventEmitter implements IRouter { return check(this.routes, _parts); } - private navigate(path: string, key: string | null | undefined, emitChange = true) { + private navigate(path: string, key: string | null | undefined, emitChange = true, _redirected = false): Resolved { const beforePath = this.currentPath; this.currentPath = path; @@ -300,6 +347,20 @@ export class Router extends EventEmitter implements IRouter { throw new Error('no route found for: ' + path); } + if ('redirect' in res.route) { + let redirectPath: string; + if (typeof res.route.redirect === 'function') { + redirectPath = res.route.redirect(res.props); + } else { + redirectPath = res.route.redirect + (res._parsedRoute.queryString ? '?' + res._parsedRoute.queryString : '') + (res._parsedRoute.hash ? '#' + res._parsedRoute.hash : ''); + } + if (_DEV_) console.log('Redirecting to: ', redirectPath); + if (_redirected && this.redirectCount++ > 10) { + throw new Error('redirect loop detected'); + } + return this.navigate(redirectPath, null, emitChange, true); + } + if (res.route.loginRequired && !this.isLoggedIn) { res.route.component = this.notFoundPageComponent; res.props.set('showLoginPopup', true); @@ -321,7 +382,11 @@ export class Router extends EventEmitter implements IRouter { }); } - return res; + this.redirectCount = 0; + return { + ...res, + redirected: _redirected, + }; } public getCurrentPath() { @@ -345,7 +410,7 @@ export class Router extends EventEmitter implements IRouter { const res = this.navigate(path, null); this.emit('push', { beforePath, - path, + path: res._parsedRoute.fullPath, route: res.route, props: res.props, key: this.currentKey, @@ -353,7 +418,11 @@ export class Router extends EventEmitter implements IRouter { } public replace(path: string, key?: string | null) { - this.navigate(path, key); + const res = this.navigate(path, key); + this.emit('replace', { + path: res._parsedRoute.fullPath, + key: this.currentKey, + }); } } From 4535f9b41b9bb353d8a7eadd59c6239f9e519904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:33:42 +0900 Subject: [PATCH 09/36] =?UTF-8?q?fix(i18n):=20=E3=82=B9=E3=83=88=E3=83=83?= =?UTF-8?q?=E3=82=AF=E6=83=85=E5=A0=B1=E3=81=A8=E3=83=95=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=81=AE=E6=96=87=E8=A8=80=E3=82=92=E3=82=8F?= =?UTF-8?q?=E3=81=8B=E3=82=8A=E3=82=84=E3=81=99=E3=81=8F=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=20(#13085)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(i18n): ストック情報とフロー情報をわかりやすく書き直す * Update ja-JP.yml * Update ja-JP.yml --- locales/ja-JP.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b9e7ea4922..cf45c13f75 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1223,7 +1223,7 @@ _announcement: tooManyActiveAnnouncementDescription: "アクティブなお知らせが多いため、UXが低下する可能性があります。終了したお知らせはアーカイブすることを検討してください。" readConfirmTitle: "既読にしますか?" readConfirmText: "「{title}」の内容を読み、既読にします。" - shouldNotBeUsedToPresentPermanentInfo: "特に新規ユーザーのUXを損ねる可能性が高いため、ストック情報ではなくフロー情報の掲示にお知らせを使用することを推奨します。" + shouldNotBeUsedToPresentPermanentInfo: "特に新規ユーザーのUXを損ねる可能性が高いため、常時掲示するための情報ではなく、即時性が求められる情報の掲示のためにお知らせを使用することを推奨します。" dialogAnnouncementUxWarn: "ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が非常に高いため、使用は慎重に行うことを推奨します。" silence: "非通知" silenceDescription: "オンにすると、このお知らせは通知されず、既読にする必要もなくなります。" From e21cecefa1a395be58918730c07404720220128b Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Mon, 29 Jan 2024 13:39:34 +0100 Subject: [PATCH 10/36] test(frontend): load default config to start vite (#12867) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com> --- packages/frontend/vite.config.local-dev.ts | 33 +++++++++++++--------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts index 5a6f511c66..6d9488797c 100644 --- a/packages/frontend/vite.config.local-dev.ts +++ b/packages/frontend/vite.config.local-dev.ts @@ -1,5 +1,7 @@ import dns from 'dns'; +import { readFile } from 'node:fs/promises'; import { defineConfig } from 'vite'; +import * as yaml from 'js-yaml'; import locales from '../../locales/index.js'; import { getConfig } from './vite.config.js'; @@ -7,6 +9,11 @@ dns.setDefaultResultOrder('ipv4first'); const defaultConfig = getConfig(); +const { port } = yaml.load(await readFile('../../.config/default.yml', 'utf-8')); + +const httpUrl = `http://localhost:${port}/`; +const websocketUrl = `ws://localhost:${port}/`; + const devConfig = { // 基本の設定は vite.config.js から引き継ぐ ...defaultConfig, @@ -19,28 +26,28 @@ const devConfig = { proxy: { '/api': { changeOrigin: true, - target: 'http://localhost:3000/', + target: httpUrl, }, - '/assets': 'http://localhost:3000/', - '/static-assets': 'http://localhost:3000/', - '/client-assets': 'http://localhost:3000/', - '/files': 'http://localhost:3000/', - '/twemoji': 'http://localhost:3000/', - '/fluent-emoji': 'http://localhost:3000/', - '/sw.js': 'http://localhost:3000/', + '/assets': httpUrl, + '/static-assets': httpUrl, + '/client-assets': httpUrl, + '/files': httpUrl, + '/twemoji': httpUrl, + '/fluent-emoji': httpUrl, + '/sw.js': httpUrl, '/streaming': { - target: 'ws://localhost:3000/', + target: websocketUrl, ws: true, }, - '/favicon.ico': 'http://localhost:3000/', + '/favicon.ico': httpUrl, '/identicon': { - target: 'http://localhost:3000/', + target: httpUrl, rewrite(path) { return path.replace('@localhost:5173', ''); }, }, - '/url': 'http://localhost:3000', - '/proxy': 'http://localhost:3000', + '/url': httpUrl, + '/proxy': httpUrl, }, }, build: { From 9ac2c36d76337e5d348f28508488186732f1558d Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 30 Jan 2024 15:01:24 +0900 Subject: [PATCH 11/36] =?UTF-8?q?iOS=E3=81=A7=E5=A4=A7=E3=81=8D=E3=81=AA?= =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=82=92=E5=A4=89=E6=8F=9B=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20(#13109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix https://github.com/misskey-dev/misskey/issues/12026 --- CHANGELOG.md | 1 + packages/frontend/package.json | 2 +- pnpm-lock.yaml | 67 ++++++++++++---------------------- 3 files changed, 26 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 345bd92c38..a77a95c025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ - Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正 - Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正 - Enhance: ページ遷移時にPlayerを閉じるように +- Fix: iOSで大きな画像を変換してアップロードできない問題を修正 ### Server - Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました diff --git a/packages/frontend/package.json b/packages/frontend/package.json index eeac6dcea3..dc3b699bcc 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -20,7 +20,7 @@ "@discordapp/twemoji": "15.0.2", "@github/webauthn-json": "2.1.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", - "@misskey-dev/browser-image-resizer": "2.2.1-misskey.10", + "@misskey-dev/browser-image-resizer": "2024.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "5.0.5", "@rollup/pluginutils": "5.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa466569ef..3b2d8d7e26 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -677,8 +677,8 @@ importers: specifier: 0.1.0-alpha-3 version: 0.1.0-alpha-3 '@misskey-dev/browser-image-resizer': - specifier: 2.2.1-misskey.10 - version: 2.2.1-misskey.10 + specifier: 2024.1.0 + version: 2024.1.0 '@rollup/plugin-json': specifier: 6.1.0 version: 6.1.0(rollup@4.9.6) @@ -831,7 +831,7 @@ importers: version: 1.7.2(vue@3.4.15) vite: specifier: 5.0.12 - version: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + version: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) vue: specifier: 3.4.15 version: 3.4.15(typescript@5.3.3) @@ -1009,7 +1009,7 @@ importers: version: 1.0.3 vitest: specifier: 0.34.6 - version: 0.34.6(happy-dom@10.0.3)(sass@1.70.0) + version: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) vitest-fetch-mock: specifier: 0.2.2 version: 0.2.2(vitest@0.34.6) @@ -4711,7 +4711,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.3.3) typescript: 5.3.3 - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) dev: true /@jridgewell/gen-mapping@0.3.2: @@ -4735,7 +4735,6 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.3.2 '@jridgewell/trace-mapping': 0.3.18 - dev: false /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} @@ -4853,8 +4852,8 @@ packages: resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} dev: true - /@misskey-dev/browser-image-resizer@2.2.1-misskey.10: - resolution: {integrity: sha512-Spjiwa8brffhz4FiYrZ8VoPRyPPRzcdaIzLVb8oMnD9YGU3uzcX/CcZ08okFhrUR/N6IlQM86r5dNH/yY5Uyjg==} + /@misskey-dev/browser-image-resizer@2024.1.0: + resolution: {integrity: sha512-4EnO0zLW5NDtng3Gaz5MuT761uiuoOuplwX18wBqgj8w56LTU5BjLn/vbHwDIIe0j2gwqDYhMb7bDjmr1/Fomg==} dev: false /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.11.0)(@typescript-eslint/parser@6.11.0)(eslint-plugin-import@2.29.1)(eslint@8.53.0): @@ -6772,7 +6771,7 @@ packages: magic-string: 0.30.5 rollup: 3.29.4 typescript: 5.3.3 - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - encoding - supports-color @@ -6977,7 +6976,7 @@ packages: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.0 - ws: 8.16.0 + ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -7146,7 +7145,7 @@ packages: react: 18.2.0 react-docgen: 7.0.1 react-dom: 18.2.0(react@18.2.0) - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -7272,7 +7271,7 @@ packages: '@storybook/vue3': 7.6.10(vue@3.4.15) '@vitejs/plugin-vue': 4.5.2(vite@5.0.12)(vue@3.4.15) magic-string: 0.30.5 - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) vue-docgen-api: 4.64.1(vue@3.4.15) transitivePeerDependencies: - '@preact/preset-vite' @@ -7772,7 +7771,7 @@ packages: dom-accessibility-api: 0.5.16 lodash: 4.17.21 redent: 3.0.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) dev: true /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0): @@ -8707,7 +8706,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color dev: true @@ -8719,7 +8718,7 @@ packages: vite: ^4.0.0 || ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) vue: 3.4.15(typescript@5.3.3) dev: true @@ -8730,7 +8729,7 @@ packages: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) vue: 3.4.15(typescript@5.3.3) dev: false @@ -8750,7 +8749,7 @@ packages: std-env: 3.7.0 test-exclude: 6.0.0 v8-to-istanbul: 9.2.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color dev: true @@ -9889,7 +9888,6 @@ packages: requiresBuild: true dependencies: node-gyp-build: 4.6.0 - dev: false /bullmq@5.1.4: resolution: {integrity: sha512-j/AjaPc8BhyrH7b2MyZpi4cUtGH8TJTxonZUmXEefmKU8z5DcldzmlXPief0P4+qvN0A7qwWZH3n0F+GsWgQkg==} @@ -10434,7 +10432,6 @@ packages: /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false /commander@6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} @@ -15624,7 +15621,6 @@ packages: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} hasBin: true requiresBuild: true - dev: false /node-gyp@10.0.1: resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==} @@ -18932,7 +18928,6 @@ packages: acorn: 8.11.3 commander: 2.20.3 source-map-support: 0.5.21 - dev: false /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} @@ -19654,7 +19649,6 @@ packages: requiresBuild: true dependencies: node-gyp-build: 4.6.0 - dev: false /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -19746,7 +19740,7 @@ packages: core-util-is: 1.0.2 extsprintf: 1.3.0 - /vite-node@0.34.6(@types/node@20.11.5)(sass@1.70.0): + /vite-node@0.34.6(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0): resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19756,7 +19750,7 @@ packages: mlly: 1.5.0 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -19772,7 +19766,7 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@5.0.12(@types/node@20.11.5)(sass@1.70.0): + /vite@5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0): resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -19805,6 +19799,7 @@ packages: postcss: 8.4.33 rollup: 4.9.6 sass: 1.70.0 + terser: 5.27.0 optionalDependencies: fsevents: 2.3.3 @@ -19815,12 +19810,12 @@ packages: vitest: '>=0.16.0' dependencies: cross-fetch: 3.1.5 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - encoding dev: true - /vitest@0.34.6(happy-dom@10.0.3)(sass@1.70.0): + /vitest@0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0): resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19873,8 +19868,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.6.0 tinypool: 0.7.0 - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) - vite-node: 0.34.6(@types/node@20.11.5)(sass@1.70.0) + vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) + vite-node: 0.34.6(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -20275,19 +20270,6 @@ packages: async-limiter: 1.0.1 dev: true - /ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: true - /ws@8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3): resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} @@ -20302,7 +20284,6 @@ packages: dependencies: bufferutil: 4.0.7 utf-8-validate: 6.0.3 - dev: false /xev@3.0.2: resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==} From a6a91fec3af5314472b0a70402c1ef7e73a478ef Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Tue, 30 Jan 2024 19:53:53 +0900 Subject: [PATCH 12/36] =?UTF-8?q?refactor:=20frontend=E3=81=AEcomponents?= =?UTF-8?q?=E3=81=AE=E5=9E=8B=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E6=94=B9?= =?UTF-8?q?=E5=96=84=20(#12926)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add: safeFloatParserを追加 * fix: 欠けていた型を追加 * refactor: pageBlockTypesをjson-schemaに移植 * refactor: components/global内の型エラーが出ている箇所を修正 * lint: fix null check style * refactor: fix type error * refactor: fix some type errors * fix: 翻訳が抜けていた箇所を修正 * refactor: getJsonSchemaで正しいスキーマが返されるように修正 * fix: MkChartの型エラーとbytesオプションが機能していない問題を修正 * fix(misskey-js): `drive`->`folderUpdated`のpayloadの型が間違っていたのを修正 * refactor: fix some type errors * change: Captcha読み込み中の文言をLoadingに変更 * refactor(backend/misskey-js): MainEventの型を改善 * refactor: chartjs-plugin-gradientが二重でpluginに登録されていたのを修正 * update: misskey-js.api.md * refactor: fix some type errors * fix: backendのtypecheckが落ちていたのを修正 * update: misskey-js.api.md * add: json-schemaのnoteにpollの型定義を追加 * refactor: noteのjson-schemaの型を改善 * refactor: MkPoll * refactor: fix some type errors * change: UserLiteにisLockedを持たせるように * fix: notificationスキーマにroleが含まれていないのを修正 * Revert "change: UserLiteにisLockedを持たせるように" This reverts commit 1bb0c8e7a9b19a4e9f21bf7381712b98f27672a5. * fix: フォロー通知から鍵垢へのフォローを行うと処理中のまま止まってしまう問題を修正 * refactor: noteスキーマのvisibilityにenumを追加 * change: deepCloneのCloneableTypeにundefinedを追加 * refactor: fix some type errors * refactor: `allowEmpty: false`を使用していた箇所を`minLength: 1`に置き換え * enhance: API 'retension' のresponseの型を追加 * fix: Chart関連のtooltipが正しい位置に表示されない問題を修正 * refactor: fix some type errors * fix: 型情報が不足していたのを修正 * enhance: announcementスキーマにenumを追加 * enhance: ロールポリシーの型定義をRoleServiceからjson-schemaに移植 * refactor: policiesを`ref: RolePolicies`に統一 * fix: API `meta` のレスポンスの型にpoliciesが含まれていないのを修正 * refactor: fix some type errors * fix: backendのlintが落ちているのを修正 * fix: MkFoldableSectionの開閉時のanimationが適用されていない問題を修正 * fix: backendのtypecheckが落ちているのを修正 * update: run build-misskey-js-with-types * fix: MkDialogのmount時に文字数制限の判定が行われない問題を修正 * update: CHANGELOG.md * refactor: MkUserSelectDialogの型を改善 * fix: deepCloneでundefinedはcloneしないように (#9207) * change: frontendのcloneをbackend側にも反映 * update: CHANGELOG.md * fix: RoleServiceからPackを通して型RolePoliciesに依存させないように * Update packages/frontend/src/scripts/get-note-summary.ts * revert RoleService.ts changes * change: optional chaining -> non-null assertion * remove: unused import * fix: propsで渡されたuserがUserLiteの場合に意図しない動作になってしまうのを修正 * change: fix null check style * refactor: fix type error * change: fix null check style * Update packages/frontend/src/components/MkDrive.vue Co-authored-by: syuilo * refactor: css moduleでglobalを使わないように * refactor: roleのiconUrlは必ず存在するものとして扱うように * enhance: MenuButtonのactiveにcomputedを受け付けられるように * Update packages/frontend/src/components/MkNotePreview.vue * Update MkWindow.vue * refactor: notification.noteは必ず存在するものとして扱うように * Update packages/frontend/src/components/MkNotification.vue Co-authored-by: syuilo * fix: MkSignupDialogでdoneのemit時にresを含んでいなかったのを修正 * Update packages/frontend/src/scripts/clone.ts Co-authored-by: syuilo * refactor: 不要な返り値の型を削除 * refactor: 不要なnullチェックを削除 * update: misskey-js-autogen * update: clone.ts * refactor * Update MkNotification.vue * Update MkNotification.vue * :v: * Update MkNotification.vue * Update MkNotification.vue * Update MkNotification.vue * Update MkNotifications.vue * Update MkUserSetupDialog.Profile.vue * Update MkUserCardMini.vue * :v: * Update MkMenu.vue --------- Co-authored-by: syuilo --- CHANGELOG.md | 6 + .../backend/src/core/GlobalEventService.ts | 6 +- packages/backend/src/core/chart/core.ts | 28 +- .../src/core/entities/NoteEntityService.ts | 2 +- packages/backend/src/misc/clone.ts | 4 +- packages/backend/src/misc/json-schema.ts | 9 +- packages/backend/src/models/Announcement.ts | 4 +- .../src/models/json-schema/announcement.ts | 2 + .../backend/src/models/json-schema/note.ts | 59 +- .../src/models/json-schema/notification.ts | 418 ++++++++++-- .../backend/src/models/json-schema/page.ts | 102 +++ .../backend/src/models/json-schema/role.ts | 152 +++-- .../backend/src/models/json-schema/user.ts | 99 +-- packages/backend/src/server/api/endpoints.ts | 5 +- .../backend/src/server/api/endpoints/meta.ts | 5 + .../src/server/api/endpoints/retention.ts | 26 + packages/frontend/@types/global.d.ts | 5 + .../src/components/MkAbuseReportWindow.vue | 2 +- .../src/components/MkAchievements.vue | 2 +- .../src/components/MkAnnouncementDialog.vue | 4 +- packages/frontend/src/components/MkAsUi.vue | 42 +- packages/frontend/src/components/MkButton.vue | 2 +- .../frontend/src/components/MkCaptcha.vue | 3 +- packages/frontend/src/components/MkChart.vue | 49 +- .../frontend/src/components/MkChartLegend.vue | 14 +- .../frontend/src/components/MkColorInput.vue | 4 +- .../frontend/src/components/MkContextMenu.vue | 10 +- .../frontend/src/components/MkCwButton.vue | 22 +- .../src/components/MkDateSeparatedList.vue | 42 +- packages/frontend/src/components/MkDialog.vue | 6 +- .../src/components/MkDrive.folder.vue | 2 +- packages/frontend/src/components/MkDrive.vue | 9 +- .../src/components/MkEmojiPicker.section.vue | 6 +- .../frontend/src/components/MkEmojiPicker.vue | 4 +- .../components/MkFileCaptionEditWindow.vue | 4 +- .../src/components/MkFileListForAdmin.vue | 2 +- .../src/components/MkFoldableSection.vue | 47 +- packages/frontend/src/components/MkFolder.vue | 4 +- .../src/components/MkForgotPassword.vue | 4 +- .../frontend/src/components/MkFormDialog.vue | 9 +- .../src/components/MkGalleryPostPreview.vue | 4 +- .../frontend/src/components/MkHeatmap.vue | 27 +- .../src/components/MkImgWithBlurhash.vue | 2 +- packages/frontend/src/components/MkInput.vue | 29 +- .../src/components/MkInstanceStats.vue | 34 +- .../src/components/MkInstanceTicker.vue | 8 +- .../frontend/src/components/MkLaunchPad.vue | 4 +- .../frontend/src/components/MkMarquee.vue | 1 + .../frontend/src/components/MkMediaList.vue | 17 +- .../frontend/src/components/MkMenu.child.vue | 5 +- packages/frontend/src/components/MkMenu.vue | 28 +- .../frontend/src/components/MkMiniChart.vue | 4 +- .../frontend/src/components/MkModalWindow.vue | 4 +- packages/frontend/src/components/MkNote.vue | 64 +- .../src/components/MkNoteDetailed.vue | 55 +- .../frontend/src/components/MkNoteHeader.vue | 2 +- .../frontend/src/components/MkNotePreview.vue | 10 +- .../src/components/MkNotification.vue | 39 +- .../src/components/MkNotifications.vue | 4 +- packages/frontend/src/components/MkOmit.vue | 4 +- .../frontend/src/components/MkPagePreview.vue | 2 +- .../frontend/src/components/MkPageWindow.vue | 14 +- .../frontend/src/components/MkPagination.vue | 6 +- packages/frontend/src/components/MkPoll.vue | 38 +- .../frontend/src/components/MkPollEditor.vue | 42 +- .../frontend/src/components/MkPostForm.vue | 83 +-- .../src/components/MkPostFormAttaches.vue | 2 +- .../src/components/MkPostFormDialog.vue | 12 +- .../MkPushNotificationAllowButton.vue | 2 +- packages/frontend/src/components/MkRadios.vue | 2 +- packages/frontend/src/components/MkRange.vue | 4 +- .../components/MkReactionsViewer.reaction.vue | 7 +- .../src/components/MkRetentionHeatmap.vue | 30 +- .../src/components/MkRetentionLineChart.vue | 12 +- packages/frontend/src/components/MkSelect.vue | 34 +- packages/frontend/src/components/MkSignin.vue | 1 + .../src/components/MkSignupDialog.form.vue | 3 +- .../src/components/MkSignupDialog.rules.vue | 6 +- .../src/components/MkSignupDialog.vue | 14 +- .../frontend/src/components/MkSparkle.vue | 7 +- .../src/components/MkSubNoteContent.vue | 6 +- .../src/components/MkSwitch.button.vue | 2 +- packages/frontend/src/components/MkTab.vue | 12 +- .../frontend/src/components/MkTagCloud.vue | 2 +- .../frontend/src/components/MkTextarea.vue | 20 +- .../frontend/src/components/MkTimeline.vue | 11 +- .../frontend/src/components/MkTooltip.vue | 2 +- .../src/components/MkTutorialDialog.Note.vue | 5 +- .../components/MkTutorialDialog.PostNote.vue | 2 +- .../components/MkTutorialDialog.Sensitive.vue | 2 +- .../MkUserAnnouncementEditDialog.vue | 34 +- .../src/components/MkUserCardMini.vue | 104 ++- .../frontend/src/components/MkUserPopup.vue | 1 + .../src/components/MkUserSelectDialog.vue | 17 +- .../components/MkUserSetupDialog.Follow.vue | 28 +- .../components/MkUserSetupDialog.Profile.vue | 4 +- .../src/components/MkVisibilityPicker.vue | 2 +- .../MkVisitorDashboard.ActiveUsersChart.vue | 17 +- .../src/components/MkVisitorDashboard.vue | 8 +- .../src/components/MkWaitingDialog.vue | 2 +- .../frontend/src/components/MkWidgets.vue | 8 +- packages/frontend/src/components/MkWindow.vue | 76 ++- .../src/components/MkYouTubePlayer.vue | 2 +- .../frontend/src/components/global/MkAcct.vue | 2 +- .../src/components/global/MkAvatar.vue | 9 +- .../src/components/global/MkCustomEmoji.vue | 2 +- .../global/MkMisskeyFlavoredMarkdown.ts | 35 +- .../components/global/MkPageHeader.tabs.vue | 12 +- .../components/global/MkStickyContainer.vue | 23 +- .../frontend/src/components/global/MkTime.vue | 2 +- .../src/components/page/block.type.ts | 34 - .../src/components/page/page.block.vue | 3 +- .../src/components/page/page.image.vue | 12 +- .../src/components/page/page.note.vue | 4 +- .../src/components/page/page.section.vue | 3 +- .../src/components/page/page.text.vue | 5 +- packages/frontend/src/nirax.ts | 3 +- packages/frontend/src/os.ts | 2 +- packages/frontend/src/pages/admin/files.vue | 2 +- packages/frontend/src/pages/share.vue | 2 +- packages/frontend/src/pages/theme-editor.vue | 2 +- .../frontend/src/pages/welcome.timeline.vue | 2 +- packages/frontend/src/scripts/autocomplete.ts | 4 +- packages/frontend/src/scripts/clone.ts | 4 +- .../frontend/src/scripts/get-note-menu.ts | 15 +- .../frontend/src/scripts/get-note-summary.ts | 6 +- .../frontend/src/scripts/popup-position.ts | 2 +- .../frontend/src/scripts/reaction-picker.ts | 2 +- packages/frontend/src/scripts/safe-parse.ts | 11 + .../frontend/src/scripts/use-note-capture.ts | 6 +- packages/frontend/src/types/menu.ts | 4 +- packages/frontend/src/ui/deck.vue | 2 +- packages/misskey-js/etc/misskey-js.api.md | 19 +- .../misskey-js/src/autogen/apiClientJSDoc.ts | 4 +- packages/misskey-js/src/autogen/endpoint.ts | 4 +- packages/misskey-js/src/autogen/entities.ts | 4 +- packages/misskey-js/src/autogen/models.ts | 6 +- packages/misskey-js/src/autogen/types.ts | 612 +++++++++++------- packages/misskey-js/src/streaming.types.ts | 15 +- 139 files changed, 1944 insertions(+), 1193 deletions(-) delete mode 100644 packages/frontend/src/components/page/block.type.ts create mode 100644 packages/frontend/src/scripts/safe-parse.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a77a95c025..9bfb12d25e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,12 @@ - Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正 - Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正 - Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正 +- Fix: Renoteのキーボードショートカットが機能していなかった問題を修正 +- Fix: 投稿フォームでアンケートの日時指定をした状態で再読み込みをすると期日が復元されない問題を修正 +- Fix: アンケートを設定したノートを「削除して編集」をするとアンケートの期日が引き継がれず、リセットされてしまう問題を修正 +- Fix: デッキのプロファイル作成時に名前を空にできる問題を修正 +- Fix: テーマ作成時に名称が空欄でも作成できてしまう問題を修正 +- Fix: プラグインで`Plugin:register_note_post_interruptor`を使用すると、ノートが投稿できなくなる問題を修正 - Enhance: ページ遷移時にPlayerを閉じるように - Fix: iOSで大きな画像を変換してアップロードできない問題を修正 diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 5b4c8cb44f..6a72671665 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -54,9 +54,9 @@ export interface MainEventTypes { reply: Packed<'Note'>; renote: Packed<'Note'>; follow: Packed<'UserDetailedNotMe'>; - followed: Packed<'User'>; - unfollow: Packed<'User'>; - meUpdated: Packed<'User'>; + followed: Packed<'UserDetailed' | 'UserLite'>; + unfollow: Packed<'UserDetailed'>; + meUpdated: Packed<'UserDetailed'>; pageEvent: { pageId: MiPage['id']; event: string; diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index 8d0a89f2d6..b1cde2f6e2 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -94,6 +94,29 @@ type ToJsonSchema = { }; export function getJsonSchema(schema: S): ToJsonSchema>> { + const unflatten = (str: string, parent: Record) => { + const keys = str.split('.'); + const key = keys.shift(); + const nextKey = keys[0]; + + if (key == null) return; + + if (parent.properties[key] == null) { + parent.properties[key] = nextKey ? { + type: 'object', + properties: {}, + required: [], + } : { + type: 'array', + items: { + type: 'number', + }, + }; + } + + if (nextKey) unflatten(keys.join('.'), parent.properties[key] as Record); + }; + const jsonSchema = { type: 'object', properties: {} as Record, @@ -101,10 +124,7 @@ export function getJsonSchema(schema: S): ToJsonSchema>>; diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 1777e2cf54..03cd58917b 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -164,7 +164,7 @@ export class NoteEntityService implements OnModuleInit { return { multiple: poll.multiple, - expiresAt: poll.expiresAt, + expiresAt: poll.expiresAt?.toISOString() ?? null, choices, }; } diff --git a/packages/backend/src/misc/clone.ts b/packages/backend/src/misc/clone.ts index 9d20deac3b..52e6c825f9 100644 --- a/packages/backend/src/misc/clone.ts +++ b/packages/backend/src/misc/clone.ts @@ -6,7 +6,7 @@ // structredCloneが遅いため // SEE: http://var.blog.jp/archives/86038606.html -type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; +type Cloneable = string | number | boolean | null | undefined | { [key: string]: Cloneable } | Cloneable[]; export function deepClone(x: T): T { if (typeof x === 'object') { @@ -14,7 +14,7 @@ export function deepClone(x: T): T { if (Array.isArray(x)) return x.map(deepClone) as T; const obj = {} as Record; for (const [k, v] of Object.entries(x)) { - obj[k] = deepClone(v); + obj[k] = v === undefined ? undefined : deepClone(v); } return obj as T; } else { diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index b4f0541712..0dd8f15d9a 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -25,7 +25,7 @@ import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; import { packedHashtagSchema } from '@/models/json-schema/hashtag.js'; import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js'; -import { packedPageSchema } from '@/models/json-schema/page.js'; +import { packedPageSchema, packedPageBlockSchema } from '@/models/json-schema/page.js'; import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js'; import { packedChannelSchema } from '@/models/json-schema/channel.js'; import { packedAntennaSchema } from '@/models/json-schema/antenna.js'; @@ -37,7 +37,7 @@ import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/jso import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; import { packedSigninSchema } from '@/models/json-schema/signin.js'; -import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js'; +import { packedRoleLiteSchema, packedRoleSchema, packedRolePoliciesSchema } from '@/models/json-schema/role.js'; import { packedAdSchema } from '@/models/json-schema/ad.js'; import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js'; @@ -67,6 +67,7 @@ export const refs = { Hashtag: packedHashtagSchema, InviteCode: packedInviteCodeSchema, Page: packedPageSchema, + PageBlock: packedPageBlockSchema, Channel: packedChannelSchema, QueueCount: packedQueueCountSchema, Antenna: packedAntennaSchema, @@ -79,12 +80,16 @@ export const refs = { Signin: packedSigninSchema, RoleLite: packedRoleLiteSchema, Role: packedRoleSchema, + RolePolicies: packedRolePoliciesSchema, ReversiGameLite: packedReversiGameLiteSchema, ReversiGameDetailed: packedReversiGameDetailedSchema, }; export type Packed = SchemaType; +export type KeyOf = PropertiesToUnion; +type PropertiesToUnion

= p['properties'] extends NonNullable ? keyof p['properties'] : never; + type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any'; type StringDefToType = T extends 'null' ? null : diff --git a/packages/backend/src/models/Announcement.ts b/packages/backend/src/models/Announcement.ts index 8f8be88fed..c2d9e9878c 100644 --- a/packages/backend/src/models/Announcement.ts +++ b/packages/backend/src/models/Announcement.ts @@ -38,7 +38,7 @@ export class MiAnnouncement { length: 256, nullable: false, default: 'info', }) - public icon: string; + public icon: 'info' | 'warning' | 'error' | 'success'; // normal ... お知らせページ掲載 // banner ... お知らせページ掲載 + バナー表示 @@ -47,7 +47,7 @@ export class MiAnnouncement { length: 256, nullable: false, default: 'normal', }) - public display: string; + public display: 'normal' | 'banner' | 'dialog'; @Column('boolean', { default: false, diff --git a/packages/backend/src/models/json-schema/announcement.ts b/packages/backend/src/models/json-schema/announcement.ts index 78a98872b2..57fd7d605d 100644 --- a/packages/backend/src/models/json-schema/announcement.ts +++ b/packages/backend/src/models/json-schema/announcement.ts @@ -37,10 +37,12 @@ export const packedAnnouncementSchema = { icon: { type: 'string', optional: false, nullable: false, + enum: ['info', 'warning', 'error', 'success'], }, display: { type: 'string', optional: false, nullable: false, + enum: ['dialog', 'normal', 'banner'], }, needConfirmationToRead: { type: 'boolean', diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 2b7722129b..929f697e8a 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -69,6 +69,7 @@ export const packedNoteSchema = { visibility: { type: 'string', optional: false, nullable: false, + enum: ['public', 'home', 'followers', 'specified'], }, mentions: { type: 'array', @@ -117,6 +118,48 @@ export const packedNoteSchema = { poll: { type: 'object', optional: true, nullable: true, + properties: { + expiresAt: { + type: 'string', + optional: true, nullable: true, + format: 'date-time', + }, + multiple: { + type: 'boolean', + optional: false, nullable: false, + }, + choices: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + isVoted: { + type: 'boolean', + optional: false, nullable: false, + }, + text: { + type: 'string', + optional: false, nullable: false, + }, + votes: { + type: 'number', + optional: false, nullable: false, + }, + }, + }, + }, + }, + }, + emojis: { + type: 'object', + optional: true, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'string', + }], + }, }, channelId: { type: 'string', @@ -162,9 +205,23 @@ export const packedNoteSchema = { type: 'string', optional: false, nullable: true, }, + reactionEmojis: { + type: 'object', + optional: false, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'string', + }], + }, + }, reactions: { type: 'object', optional: false, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'number', + }], + }, }, renoteCount: { type: 'number', @@ -196,7 +253,7 @@ export const packedNoteSchema = { }, myReaction: { - type: 'object', + type: 'string', optional: true, nullable: true, }, }, diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index c6d6e84317..6286950de5 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -5,7 +5,7 @@ import { notificationTypes } from '@/types.js'; -export const packedNotificationSchema = { +const baseSchema = { type: 'object', properties: { id: { @@ -23,68 +23,368 @@ export const packedNotificationSchema = { optional: false, nullable: false, enum: [...notificationTypes, 'reaction:grouped', 'renote:grouped'], }, - user: { - type: 'object', - ref: 'UserLite', - optional: true, nullable: true, - }, - userId: { - type: 'string', - optional: true, nullable: true, - format: 'id', - }, - note: { - type: 'object', - ref: 'Note', - optional: true, nullable: true, - }, - reaction: { - type: 'string', - optional: true, nullable: true, - }, - achievement: { - type: 'string', - optional: true, nullable: false, - }, - body: { - type: 'string', - optional: true, nullable: true, - }, - header: { - type: 'string', - optional: true, nullable: true, - }, - icon: { - type: 'string', - optional: true, nullable: true, - }, - reactions: { - type: 'array', - optional: true, nullable: true, - items: { - type: 'object', - properties: { - user: { - type: 'object', - ref: 'UserLite', - optional: false, nullable: false, - }, - reaction: { - type: 'string', - optional: false, nullable: false, - }, - }, - required: ['user', 'reaction'], + }, +} as const; + +export const packedNotificationSchema = { + type: 'object', + oneOf: [{ + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['note'], }, - }, - users: { - type: 'array', - optional: true, nullable: true, - items: { + user: { type: 'object', ref: 'UserLite', optional: false, nullable: false, }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['mention'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reply'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['renote'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['quote'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reaction'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + reaction: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['pollEnded'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['follow'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['receiveFollowRequest'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['followRequestAccepted'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['roleAssigned'], + }, + role: { + type: 'object', + ref: 'Role', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['achievementEarned'], + }, + achievement: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['app'], + }, + body: { + type: 'string', + optional: false, nullable: false, + }, + header: { + type: 'string', + optional: false, nullable: false, + }, + icon: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reaction:grouped'], + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + reactions: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + properties: { + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + reaction: { + type: 'string', + optional: false, nullable: false, + }, + }, + required: ['user', 'reaction'], + }, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['renote:grouped'], + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + users: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['test'], + }, + }, + }], } as const; diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts index 9baacd6884..402db76e52 100644 --- a/packages/backend/src/models/json-schema/page.ts +++ b/packages/backend/src/models/json-schema/page.ts @@ -3,6 +3,107 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +const blockBaseSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + }, + type: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; + +const textBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['text'], + }, + text: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; + +const sectionBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['section'], + }, + title: { + type: 'string', + optional: false, nullable: false, + }, + children: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'PageBlock', + }, + }, + }, +} as const; + +const imageBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['image'], + }, + fileId: { + type: 'string', + optional: false, nullable: true, + }, + }, +} as const; + +const noteBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['note'], + }, + detailed: { + type: 'boolean', + optional: false, nullable: false, + }, + note: { + type: 'string', + optional: false, nullable: true, + }, + }, +} as const; + +export const packedPageBlockSchema = { + type: 'object', + oneOf: [ + textBlockSchema, + sectionBlockSchema, + imageBlockSchema, + noteBlockSchema, + ], +} as const; + export const packedPageSchema = { type: 'object', properties: { @@ -38,6 +139,7 @@ export const packedPageSchema = { items: { type: 'object', optional: false, nullable: false, + ref: 'PageBlock', }, }, variables: { diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index b0c6804bb8..55348d4f3d 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -1,26 +1,103 @@ -const rolePolicyValue = { +export const packedRolePoliciesSchema = { type: 'object', + optional: false, nullable: false, properties: { - value: { - oneOf: [ - { - type: 'integer', - optional: false, nullable: false, - }, - { - type: 'boolean', - optional: false, nullable: false, - }, - ], + gtlAvailable: { + type: 'boolean', + optional: false, nullable: false, }, - priority: { + ltlAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, + canPublicNote: { + type: 'boolean', + optional: false, nullable: false, + }, + canInvite: { + type: 'boolean', + optional: false, nullable: false, + }, + inviteLimit: { type: 'integer', optional: false, nullable: false, }, - useDefault: { + inviteLimitCycle: { + type: 'integer', + optional: false, nullable: false, + }, + inviteExpirationTime: { + type: 'integer', + optional: false, nullable: false, + }, + canManageCustomEmojis: { type: 'boolean', optional: false, nullable: false, }, + canManageAvatarDecorations: { + type: 'boolean', + optional: false, nullable: false, + }, + canSearchNotes: { + type: 'boolean', + optional: false, nullable: false, + }, + canUseTranslator: { + type: 'boolean', + optional: false, nullable: false, + }, + canHideAds: { + type: 'boolean', + optional: false, nullable: false, + }, + driveCapacityMb: { + type: 'integer', + optional: false, nullable: false, + }, + alwaysMarkNsfw: { + type: 'boolean', + optional: false, nullable: false, + }, + pinLimit: { + type: 'integer', + optional: false, nullable: false, + }, + antennaLimit: { + type: 'integer', + optional: false, nullable: false, + }, + wordMuteLimit: { + type: 'integer', + optional: false, nullable: false, + }, + webhookLimit: { + type: 'integer', + optional: false, nullable: false, + }, + clipLimit: { + type: 'integer', + optional: false, nullable: false, + }, + noteEachClipsLimit: { + type: 'integer', + optional: false, nullable: false, + }, + userListLimit: { + type: 'integer', + optional: false, nullable: false, + }, + userEachUserListsLimit: { + type: 'integer', + optional: false, nullable: false, + }, + rateLimitFactor: { + type: 'integer', + optional: false, nullable: false, + }, + avatarDecorationLimit: { + type: 'integer', + optional: false, nullable: false, + }, }, } as const; @@ -121,31 +198,28 @@ export const packedRoleSchema = { policies: { type: 'object', optional: false, nullable: false, - properties: { - pinLimit: rolePolicyValue, - canInvite: rolePolicyValue, - clipLimit: rolePolicyValue, - canHideAds: rolePolicyValue, - inviteLimit: rolePolicyValue, - antennaLimit: rolePolicyValue, - gtlAvailable: rolePolicyValue, - ltlAvailable: rolePolicyValue, - webhookLimit: rolePolicyValue, - canPublicNote: rolePolicyValue, - userListLimit: rolePolicyValue, - wordMuteLimit: rolePolicyValue, - alwaysMarkNsfw: rolePolicyValue, - canSearchNotes: rolePolicyValue, - driveCapacityMb: rolePolicyValue, - rateLimitFactor: rolePolicyValue, - inviteLimitCycle: rolePolicyValue, - noteEachClipsLimit: rolePolicyValue, - inviteExpirationTime: rolePolicyValue, - canManageCustomEmojis: rolePolicyValue, - userEachUserListsLimit: rolePolicyValue, - canManageAvatarDecorations: rolePolicyValue, - canUseTranslator: rolePolicyValue, - avatarDecorationLimit: rolePolicyValue, + additionalProperties: { + anyOf: [{ + type: 'object', + properties: { + value: { + oneOf: [ + { + type: 'integer', + }, + { + type: 'boolean', + }, + ], + }, + priority: { + type: 'integer', + }, + useDefault: { + type: 'boolean', + }, + }, + }], }, }, usersCount: { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 6a0d43b1ac..7447513a93 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -590,104 +590,7 @@ export const packedMeDetailedOnlySchema = { policies: { type: 'object', nullable: false, optional: false, - properties: { - gtlAvailable: { - type: 'boolean', - nullable: false, optional: false, - }, - ltlAvailable: { - type: 'boolean', - nullable: false, optional: false, - }, - canPublicNote: { - type: 'boolean', - nullable: false, optional: false, - }, - canInvite: { - type: 'boolean', - nullable: false, optional: false, - }, - inviteLimit: { - type: 'number', - nullable: false, optional: false, - }, - inviteLimitCycle: { - type: 'number', - nullable: false, optional: false, - }, - inviteExpirationTime: { - type: 'number', - nullable: false, optional: false, - }, - canManageCustomEmojis: { - type: 'boolean', - nullable: false, optional: false, - }, - canManageAvatarDecorations: { - type: 'boolean', - nullable: false, optional: false, - }, - canSearchNotes: { - type: 'boolean', - nullable: false, optional: false, - }, - canUseTranslator: { - type: 'boolean', - nullable: false, optional: false, - }, - canHideAds: { - type: 'boolean', - nullable: false, optional: false, - }, - driveCapacityMb: { - type: 'number', - nullable: false, optional: false, - }, - alwaysMarkNsfw: { - type: 'boolean', - nullable: false, optional: false, - }, - pinLimit: { - type: 'number', - nullable: false, optional: false, - }, - antennaLimit: { - type: 'number', - nullable: false, optional: false, - }, - wordMuteLimit: { - type: 'number', - nullable: false, optional: false, - }, - webhookLimit: { - type: 'number', - nullable: false, optional: false, - }, - clipLimit: { - type: 'number', - nullable: false, optional: false, - }, - noteEachClipsLimit: { - type: 'number', - nullable: false, optional: false, - }, - userListLimit: { - type: 'number', - nullable: false, optional: false, - }, - userEachUserListsLimit: { - type: 'number', - nullable: false, optional: false, - }, - rateLimitFactor: { - type: 'number', - nullable: false, optional: false, - }, - avatarDecorationLimit: { - type: 'number', - nullable: false, optional: false, - }, - }, + ref: 'RolePolicies', }, //#region secrets email: { diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 4a88216d06..75e6395f18 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -4,8 +4,7 @@ */ import { permissions } from 'misskey-js'; -import type { Schema } from '@/misc/json-schema.js'; -import { RolePolicies } from '@/core/RoleService.js'; +import type { KeyOf, Schema } from '@/misc/json-schema.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; @@ -776,7 +775,7 @@ interface IEndpointMetaBase { */ readonly requireAdmin?: boolean; - readonly requireRolePolicy?: keyof RolePolicies; + readonly requireRolePolicy?: KeyOf<'RolePolicies'>; /** * 引っ越し済みのユーザーによるリクエストを禁止するか diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 529e82678d..e1d3473482 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -303,6 +303,11 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + policies: { + type: 'object', + optional: false, nullable: false, + ref: 'RolePolicies', + }, }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/retention.ts b/packages/backend/src/server/api/endpoints/retention.ts index dac6d65407..2631693139 100644 --- a/packages/backend/src/server/api/endpoints/retention.ts +++ b/packages/backend/src/server/api/endpoints/retention.ts @@ -14,6 +14,32 @@ export const meta = { requireCredential: false, res: { + type: 'array', + items: { + type: 'object', + properties: { + createdAt: { + type: 'string', + format: 'date-time', + }, + users: { + type: 'number', + }, + data: { + type: 'object', + additionalProperties: { + anyOf: [{ + type: 'number', + }], + }, + }, + }, + required: [ + 'createdAt', + 'users', + 'data', + ], + }, }, allowGet: true, diff --git a/packages/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts index 7d9335cc52..936e74decf 100644 --- a/packages/frontend/@types/global.d.ts +++ b/packages/frontend/@types/global.d.ts @@ -16,3 +16,8 @@ declare const _DATA_TRANSFER_DECK_COLUMN_: string; // for dev-mode declare const _LANGS_FULL_: string[][]; + +// TagCanvas +interface Window { + TagCanvas: any; +} diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index 7814681ea2..39745a97ce 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -39,7 +39,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - user: Misskey.entities.User; + user: Misskey.entities.UserDetailed; initialComment?: string; }>(); diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index 1137eaf970..ff8a9fa1a5 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only - {{ c.text }} - + {{ c.text }} + {{ c.text }}

{{ button.text }} @@ -20,19 +20,19 @@ SPDX-License-Identifier: AGPL-3.0-only - + - + - + - + @@ -42,8 +42,8 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkSelect from '@/components/MkSelect.vue'; -import { AsUiComponent } from '@/scripts/aiscript/ui.js'; +import { AsUiComponent, AsUiRoot, AsUiPostFormButton } from '@/scripts/aiscript/ui.js'; import MkFolder from '@/components/MkFolder.vue'; import MkPostForm from '@/components/MkPostForm.vue'; @@ -85,20 +85,32 @@ const props = withDefaults(defineProps<{ const c = props.component; function g(id) { - return props.components.find(x => x.value.id === id).value; + const v = props.components.find(x => x.value.id === id)?.value; + if (v) return v; + + return { + id: 'dummy', + type: 'root', + children: [], + } as AsUiRoot; } -const valueForSwitch = ref(c.default ?? false); +const valueForSwitch = ref('default' in c && typeof c.default === 'boolean' ? c.default : false); function onSwitchUpdate(v) { valueForSwitch.value = v; - if (c.onChange) c.onChange(v); + if ('onChange' in c && c.onChange) { + c.onChange(v as never); + } } function openPostForm() { + const form = (c as AsUiPostFormButton).form; + if (!form) return; + os.post({ - initialText: c.form.text, - initialCw: c.form.cw, + initialText: form.text, + initialCw: form.cw, instant: true, }); } diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 8d4631968d..70de6a851a 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index f60c721eae..7aa08cf51f 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only