summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-10-20 14:48:16 +0900
committersyuilo <4439005+syuilo@users.noreply.github.com>2025-10-20 14:48:16 +0900
commitaadc7bf61a7b618602414ac38ec882856108b937 (patch)
treeba870e7d229260879147b5d7eebb0f2631a9d4be
parentadd note (diff)
parentUpdate CHANGELOG.md (follow-up of #16672) (diff)
downloadmisskey-aadc7bf61a7b618602414ac38ec882856108b937.tar.gz
misskey-aadc7bf61a7b618602414ac38ec882856108b937.tar.bz2
misskey-aadc7bf61a7b618602414ac38ec882856108b937.zip
Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop
Diffstat (limited to '')
-rw-r--r--CHANGELOG.md3
-rw-r--r--locales/ca-ES.yml2
-rw-r--r--locales/es-ES.yml2
-rw-r--r--locales/index.d.ts4
-rw-r--r--locales/it-IT.yml18
-rw-r--r--locales/ja-JP.yml1
-rw-r--r--locales/ko-KR.yml2
-rw-r--r--locales/zh-CN.yml2
-rw-r--r--locales/zh-TW.yml3
-rw-r--r--package.json5
-rw-r--r--packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js16
-rw-r--r--packages/backend/migration/1760790899857-unnecessary-null-default.js26
-rw-r--r--packages/backend/src/core/entities/UserEntityService.ts4
-rw-r--r--packages/backend/src/models/Meta.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts5
-rw-r--r--packages/frontend/src/directives/adaptive-bg.ts6
-rw-r--r--packages/frontend/src/directives/adaptive-border.ts8
-rw-r--r--packages/frontend/src/directives/anim.ts8
-rw-r--r--packages/frontend/src/directives/appear.ts14
-rw-r--r--packages/frontend/src/directives/click-anime.ts6
-rw-r--r--packages/frontend/src/directives/follow-append.ts12
-rw-r--r--packages/frontend/src/directives/get-size.ts10
-rw-r--r--packages/frontend/src/directives/hotkey.ts11
-rw-r--r--packages/frontend/src/directives/index.ts66
-rw-r--r--packages/frontend/src/directives/panel.ts6
-rw-r--r--packages/frontend/src/directives/ripple.ts7
-rw-r--r--packages/frontend/src/directives/tooltip.ts48
-rw-r--r--packages/frontend/src/directives/user-preview.ts50
-rw-r--r--packages/frontend/src/pages/admin/performance.vue18
-rw-r--r--packages/frontend/src/pages/settings/apps.vue3
-rw-r--r--packages/frontend/src/pages/user/home.vue25
-rw-r--r--packages/frontend/src/ui/deck.vue2
-rw-r--r--packages/frontend/src/ui/deck/chat-column.vue11
-rw-r--r--packages/misskey-js/package.json2
-rw-r--r--packages/misskey-js/src/autogen/types.ts2
-rw-r--r--patches/.gitkeep0
-rw-r--r--patches/typeorm.patch17
-rw-r--r--pnpm-lock.yaml9
39 files changed, 290 insertions, 154 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 96b9554c6a..3e024a3043 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
## 2025.10.1
### General
+- Enhance: リモートユーザーに付与したロールバッジを表示できるように(オプトイン)
+ パフォーマンス上の問題からデフォルトで無効化されています。「コントロールパネル > パフォーマンス」から有効化できます。
- 依存関係の更新
### Client
@@ -9,6 +11,7 @@
- Fix: バナー画像の幅が表示領域と一致していない問題を修正
- Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正
- Fix: ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正
+- Fix: ロールポリシーによりダイレクトメッセージが無効化されている際のデッキのダイレクトメッセージカラムの挙動を改善
### Server
-
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 0f7152ba00..27f1b4a8fc 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -775,6 +775,7 @@ lockedAccountInfo: "Tret que establiu la visibilitat de la nota a \"Només segui
alwaysMarkSensitive: "Marcar com a sensible per defecte"
loadRawImages: "Carregar les imatges originals en comptes de miniatures "
disableShowingAnimatedImages: "No reproduir imatges animades"
+disableShowingAnimatedImages_caption: "Si les imatges animades no es reprodueixen, independentment d'aquesta configuració, és possible que la configuració d'accessibilitat del navegador i el sistema operatiu, els modes d'estalvi d'energia i similars estiguin interferint."
highlightSensitiveMedia: "Ressalta els medis marcats com a sensibles"
verificationEmailSent: "S'ha enviat un correu electrònic de verificació. Fes clic a l'enllaç per completar la verificació."
notSet: "Sense definir"
@@ -1171,6 +1172,7 @@ installed: "Instal·lats "
branding: "Marca"
enableServerMachineStats: "Publicar estadístiques del maquinari del servidor"
enableIdenticonGeneration: "Activar la generació d'icones d'identificació "
+showRoleBadgesOfRemoteUsers: "Mostrar insígnies de rols d'instàncies remotes "
turnOffToImprovePerformance: "Desactivant aquesta opció es pot millorar el rendiment."
createInviteCode: "Crear codi d'invitació "
createWithOptions: "Crear invitació amb opcions"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index e0fcf1126e..d851ac9f41 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -775,6 +775,7 @@ lockedAccountInfo: "A menos que configures la visibilidad de tus notas como \"S
alwaysMarkSensitive: "Marcar los medios de comunicación como contenido sensible por defecto"
loadRawImages: "Cargar las imágenes originales en lugar de mostrar las miniaturas"
disableShowingAnimatedImages: "No reproducir imágenes animadas"
+disableShowingAnimatedImages_caption: "Si las imágenes animadas no se reproducen independientemente de esta configuración, es posible que la configuración de accesibilidad del navegador o del sistema operativo, los modos de ahorro de energía o funciones similares estén interfiriendo."
highlightSensitiveMedia: "Resaltar medios marcados como sensibles"
verificationEmailSent: "Se le ha enviado un correo electrónico de confirmación. Por favor, acceda al enlace proporcionado en el correo electrónico para completar la configuración."
notSet: "Sin especificar"
@@ -1171,6 +1172,7 @@ installed: "Instalado"
branding: "Marca"
enableServerMachineStats: "Publicar estadísticas de hardware del servidor"
enableIdenticonGeneration: "Activar generación de identicon por usuario"
+showRoleBadgesOfRemoteUsers: "Mostrar insignias de rol de usuarios remotos"
turnOffToImprovePerformance: "Desactivar esto puede aumentar el rendimiento."
createInviteCode: "Generar invitación"
createWithOptions: "Generar con opciones"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index b54763a8a2..d79db121db 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4707,6 +4707,10 @@ export interface Locale extends ILocale {
*/
"enableIdenticonGeneration": string;
/**
+ * リモートユーザーに付与したロールバッジを表示する
+ */
+ "showRoleBadgesOfRemoteUsers": string;
+ /**
* オフにするとパフォーマンスが向上します。
*/
"turnOffToImprovePerformance": string;
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index e041134be9..d5ac402bf3 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -458,7 +458,7 @@ setupOf2fa: "Impostare l'autenticazione a due fattori"
totp: "App di autenticazione a due fattori (2FA/MFA)"
totpDescription: "Puoi autenticarti inserendo un codice OTP tramite la tua App di autenticazione a due fattori (2FA/MFA)"
moderator: "Moderatore"
-moderation: "moderazione"
+moderation: "Moderazione"
moderationNote: "Promemoria di moderazione"
moderationNoteDescription: "Puoi scrivere promemoria condivisi solo tra moderatori."
addModerationNote: "Aggiungi promemoria di moderazione"
@@ -902,7 +902,7 @@ useBlurEffect: "Utilizza effetto sfocatura"
learnMore: "Per saperne di più"
misskeyUpdated: "Misskey è stato aggiornato!"
whatIsNew: "Informazioni sull'aggiornamento"
-translate: "Traduci"
+translate: "Traduzione"
translatedFrom: "Traduzione da {x}"
accountDeletionInProgress: "È in corso l'eliminazione del profilo"
usernameInfo: "Un nome per identificare univocamente il tuo profilo sull'istanza. Puoi utilizzare caratteri alfanumerici maiuscoli, minuscoli e il trattino basso (_). Non potrai cambiare nome utente in seguito."
@@ -913,7 +913,7 @@ pubSub: "Publish/Subscribe del profilo"
lastCommunication: "La comunicazione più recente"
resolved: "Risolto"
unresolved: "Non risolto"
-breakFollow: "Rimuovi Follower"
+breakFollow: "Rimuovere Follower"
breakFollowConfirm: "Vuoi davvero togliere questo Follower?"
itsOn: "Abilitato"
itsOff: "Disabilitato"
@@ -1293,7 +1293,7 @@ sensitiveMediaRevealConfirm: "Questo allegato è esplicito, vuoi vederlo?"
createdLists: "Liste create"
createdAntennas: "Antenne create"
fromX: "Da {x}"
-genEmbedCode: "Ottieni il codice di incorporamento"
+genEmbedCode: "Ottieni il codice per incorporare"
noteOfThisUser: "Elenco di Note di questo profilo"
clipNoteLimitExceeded: "Non è possibile aggiungere ulteriori Note a questa Clip."
performance: "Prestazioni"
@@ -1347,7 +1347,7 @@ postForm: "Finestra di pubblicazione"
textCount: "Il numero di caratteri"
information: "Informazioni"
chat: "Chat"
-directMessage: "Chatta con questa persona"
+directMessage: "Chattare insieme"
directMessage_short: "Messaggio"
migrateOldSettings: "Migrare le vecchie impostazioni"
migrateOldSettings_description: "Di solito, viene fatto automaticamente. Se per qualche motivo non fossero migrate con successo, è possibile avviare il processo di migrazione manualmente, sovrascrivendo le configurazioni attuali."
@@ -1385,7 +1385,7 @@ pluginsAreDisabledBecauseSafeMode: "Tutti i plugin sono disattivati, poiché la
customCssIsDisabledBecauseSafeMode: "Il CSS personalizzato non è stato applicato, poiché la modalità sicura è attiva."
themeIsDefaultBecauseSafeMode: "Quando la modalità sicura è attiva, viene utilizzato il tema predefinito. Quando la modalità sicura viene disattivata, il tema torna a essere quello precedente."
thankYouForTestingBeta: "Grazie per la tua collaborazione nella verifica delle versioni beta!"
-createUserSpecifiedNote: "Creare Nota personalizzata"
+createUserSpecifiedNote: "Crea Nota privata"
schedulePost: "Pianificare la pubblicazione"
scheduleToPostOnX: "Pianificare la pubblicazione {x}"
scheduledToPostOnX: "Pubblicazione pianificata {x}"
@@ -3052,7 +3052,7 @@ _customEmojisManager:
confirmClearEmojisDescription: "Annullare le modifiche e cancella le emoji nell'elenco. Confermi?"
confirmUploadEmojisDescription: "Caricamento sul Drive di {count} file locali. Vuoi davvero procedere?"
_embedCodeGen:
- title: "Personalizza il codice di incorporamento"
+ title: "Personalizza il codice per incorporare"
header: "Mostra la testata"
autoload: "Carica automaticamente di più (sconsigliato)"
maxHeight: "Altezza massima"
@@ -3061,8 +3061,8 @@ _embedCodeGen:
previewIsNotActual: "Poiché supera l'intervallo che può essere visualizzato in anteprima, la visualizzazione vera e propria sarà diversa quando effettivamente incorporata."
rounded: "Bordo arrotondato"
border: "Aggiungi un bordo al contenitore"
- applyToPreview: "Applica all'anteprima"
- generateCode: "Crea il codice di incorporamento"
+ applyToPreview: "Aggiorna l'anteprima"
+ generateCode: "Crea il codice per incorporare"
codeGenerated: "Codice generato"
codeGeneratedDescription: "Incolla il codice appena generato sul tuo sito web."
_selfXssPrevention:
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3c9f0a5853..5d1c37740c 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1172,6 +1172,7 @@ installed: "インストール済み"
branding: "ブランディング"
enableServerMachineStats: "サーバーのマシン情報を公開する"
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
+showRoleBadgesOfRemoteUsers: "リモートユーザーに付与したロールバッジを表示する"
turnOffToImprovePerformance: "オフにするとパフォーマンスが向上します。"
createInviteCode: "招待コードを作成"
createWithOptions: "オプションを指定して作成"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 77b6476107..1a68375a45 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -775,6 +775,7 @@ lockedAccountInfo: "팔로우를 승인으로 승인받더라도 노트의 공
alwaysMarkSensitive: "미디어를 항상 열람 주의로 설정"
loadRawImages: "첨부한 이미지의 썸네일을 원본화질로 표시"
disableShowingAnimatedImages: "움직이는 이미지를 자동으로 재생하지 않음"
+disableShowingAnimatedImages_caption: "이 설정에 상관없이 애니메이션 이미지가 재생되지 않을 때는 브라우저·OS의 액티비티 설정이나 절전 모드 설정 등이 간섭하고 있는 경우가 있습니다."
highlightSensitiveMedia: "미디어가 민감한 내용이라는 것을 알기 쉽게 표시"
verificationEmailSent: "확인 메일을 발송하였습니다. 설정을 완료하려면 메일에 첨부된 링크를 확인해 주세요."
notSet: "설정되지 않음"
@@ -1171,6 +1172,7 @@ installed: "설치됨"
branding: "브랜딩"
enableServerMachineStats: "서버의 머신 사양을 공개하기"
enableIdenticonGeneration: "유저마다의 Identicon 생성 유효화"
+showRoleBadgesOfRemoteUsers: "리모트 유저의 역할 배지 표시"
turnOffToImprovePerformance: "이 기능을 끄면 성능이 향상될 수 있습니다."
createInviteCode: "초대 코드 생성"
createWithOptions: "옵션을 지정하여 생성"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index a60aee5f97..9994e45cfc 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -775,6 +775,7 @@ lockedAccountInfo: "即使启用该功能,只要帖子可见范围不是「仅
alwaysMarkSensitive: "默认将媒体文件标记为敏感内容"
loadRawImages: "添加附件图像的缩略图时使用原始图像质量"
disableShowingAnimatedImages: "不播放动画"
+disableShowingAnimatedImages_caption: "如果即使关闭了此设置但动画仍无法播放,则可能是浏览器或操作系统的辅助功能设置,又或者是省电设置等产生了干扰。"
highlightSensitiveMedia: "高亮显示敏感媒体"
verificationEmailSent: "已发送确认电子邮件。请访问电子邮件中的链接以完成设置。"
notSet: "未设置"
@@ -1171,6 +1172,7 @@ installed: "已安装"
branding: "品牌"
enableServerMachineStats: "公开服务器硬件统计信息"
enableIdenticonGeneration: "启用生成用户 Identicon"
+showRoleBadgesOfRemoteUsers: "显示远程用户的角色徽章"
turnOffToImprovePerformance: "关闭该选项可以提高性能。"
createInviteCode: "生成邀请码"
createWithOptions: "使用选项来创建"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index cd4b91ec04..b75a3d707b 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -775,6 +775,7 @@ lockedAccountInfo: "即使追隨需要核准,除非你將貼文的可見性設
alwaysMarkSensitive: "預設標記檔案為敏感內容"
loadRawImages: "以原始圖檔顯示附件圖檔的縮圖"
disableShowingAnimatedImages: "不播放動態圖檔"
+disableShowingAnimatedImages_caption: "無論這個設定如何,如果動畫圖片無法播放,可能是因為瀏覽器或作業系統的無障礙設定、省電設定等產生了干擾。"
highlightSensitiveMedia: "強調敏感標記"
verificationEmailSent: "已發送驗證電子郵件。請點擊進入電子郵件中的連結以完成驗證。"
notSet: "未設定"
@@ -3175,7 +3176,7 @@ _uploader:
abortConfirm: "有些檔案尚未上傳,您要中止嗎?"
doneConfirm: "有些檔案尚未上傳,是否要完成上傳?"
maxFileSizeIsX: "可上傳的最大檔案大小為 {x}。"
- allowedTypes: "可上傳的檔案類型"
+ allowedTypes: "可上傳的檔案類型。"
tip: "檔案尚未上傳。您可以在此對話框中進行上傳前的確認、重新命名、壓縮、裁切等操作。準備完成後,請點選「上傳」按鈕開始上傳。\n"
_clientPerformanceIssueTip:
title: "如果覺得電池消耗過快的話"
diff --git a/package.json b/package.json
index 55286bc5a7..1376b90cfa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "misskey",
- "version": "2025.10.1-alpha.2",
+ "version": "2025.10.1-alpha.3",
"codename": "nasubi",
"repository": {
"type": "git",
@@ -85,9 +85,6 @@
"pnpm": {
"overrides": {
"@aiscript-dev/aiscript-languageserver": "-"
- },
- "patchedDependencies": {
- "typeorm": "patches/typeorm.patch"
}
}
}
diff --git a/packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js b/packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js
new file mode 100644
index 0000000000..483d35a91b
--- /dev/null
+++ b/packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class RoleBadgesRemoteUsers1760607435831 {
+ name = 'RoleBadgesRemoteUsers1760607435831'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ADD "showRoleBadgesOfRemoteUsers" boolean NOT NULL DEFAULT false`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "showRoleBadgesOfRemoteUsers"`);
+ }
+}
diff --git a/packages/backend/migration/1760790899857-unnecessary-null-default.js b/packages/backend/migration/1760790899857-unnecessary-null-default.js
new file mode 100644
index 0000000000..d34758315f
--- /dev/null
+++ b/packages/backend/migration/1760790899857-unnecessary-null-default.js
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class UnnecessaryNullDefault1760790899857 {
+ name = 'UnnecessaryNullDefault1760790899857'
+
+ /**
+ * @param {QueryRunner} queryRunner
+ */
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "userId" DROP DEFAULT`);
+ await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "systemWebhookId" DROP DEFAULT`);
+ await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "urlPreviewUserAgent" DROP DEFAULT`);
+ }
+
+ /**
+ * @param {QueryRunner} queryRunner
+ */
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "urlPreviewUserAgent" SET DEFAULT NULL`);
+ await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "systemWebhookId" SET DEFAULT NULL`);
+ await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "userId" SET DEFAULT NULL`);
+ }
+}
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index 47021359e1..ac5b855096 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -512,8 +512,8 @@ export class UserEntityService implements OnModuleInit {
} : undefined) : undefined,
emojis: this.customEmojiService.populateEmojis(user.emojis, user.host),
onlineStatus: this.getOnlineStatus(user),
- // パフォーマンス上の理由でローカルユーザーのみ
- badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then((rs) => rs
+ // パフォーマンス上の理由で、明示的に設定しない場合はローカルユーザーのみ取得
+ badgeRoles: (this.meta.showRoleBadgesOfRemoteUsers || user.host == null) ? this.roleService.getUserBadgeRoles(user.id).then((rs) => rs
.filter((r) => r.isPublic || iAmModerator)
.sort((a, b) => b.displayOrder - a.displayOrder)
.map((r) => ({
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index f8021a7a84..205c9eeb89 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -717,6 +717,11 @@ export class MiMeta {
})
public remoteNotesCleaningExpiryDaysForEachNotes: number;
+ @Column('boolean', {
+ default: false,
+ })
+ public showRoleBadgesOfRemoteUsers: boolean;
+
@Column('jsonb', {
default: { },
})
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 21099c0a8c..2c7f793584 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -593,6 +593,10 @@ export const meta = {
type: 'number',
optional: false, nullable: false,
},
+ showRoleBadgesOfRemoteUsers: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
},
},
} as const;
@@ -748,6 +752,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
enableRemoteNotesCleaning: instance.enableRemoteNotesCleaning,
remoteNotesCleaningExpiryDaysForEachNotes: instance.remoteNotesCleaningExpiryDaysForEachNotes,
remoteNotesCleaningMaxProcessingDurationInMinutes: instance.remoteNotesCleaningMaxProcessingDurationInMinutes,
+ showRoleBadgesOfRemoteUsers: instance.showRoleBadgesOfRemoteUsers,
};
});
}
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index a1a2a99d6e..b3c2cecc67 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -209,6 +209,7 @@ export const paramDef = {
enableRemoteNotesCleaning: { type: 'boolean' },
remoteNotesCleaningExpiryDaysForEachNotes: { type: 'number' },
remoteNotesCleaningMaxProcessingDurationInMinutes: { type: 'number' },
+ showRoleBadgesOfRemoteUsers: { type: 'boolean' },
},
required: [],
} as const;
@@ -743,6 +744,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.remoteNotesCleaningMaxProcessingDurationInMinutes = ps.remoteNotesCleaningMaxProcessingDurationInMinutes;
}
+ if (ps.showRoleBadgesOfRemoteUsers !== undefined) {
+ set.showRoleBadgesOfRemoteUsers = ps.showRoleBadgesOfRemoteUsers;
+ }
+
const before = await this.metaService.fetch(true);
await this.metaService.update(set);
diff --git a/packages/frontend/src/directives/adaptive-bg.ts b/packages/frontend/src/directives/adaptive-bg.ts
index a68cd1b18b..25e9ae1c9e 100644
--- a/packages/frontend/src/directives/adaptive-bg.ts
+++ b/packages/frontend/src/directives/adaptive-bg.ts
@@ -6,8 +6,8 @@
import type { Directive } from 'vue';
import { getBgColor } from '@/utility/get-bg-color.js';
-export default {
- mounted(src, binding, vn) {
+export const adaptiveBgDirective = {
+ mounted(src) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = window.getComputedStyle(src).backgroundColor;
@@ -18,4 +18,4 @@ export default {
src.style.backgroundColor = myBg;
}
},
-} as Directive;
+} as Directive<HTMLElement>;
diff --git a/packages/frontend/src/directives/adaptive-border.ts b/packages/frontend/src/directives/adaptive-border.ts
index 8072a1ffd9..749861fd94 100644
--- a/packages/frontend/src/directives/adaptive-border.ts
+++ b/packages/frontend/src/directives/adaptive-border.ts
@@ -9,8 +9,8 @@ import { globalEvents } from '@/events.js';
const handlerMap = new WeakMap<any, any>();
-export default {
- mounted(src, binding, vn) {
+export const adaptiveBorderDirective = {
+ mounted(src) {
function calc() {
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
@@ -30,7 +30,7 @@ export default {
globalEvents.on('themeChanged', calc);
},
- unmounted(src, binding, vn) {
+ unmounted(src) {
globalEvents.off('themeChanged', handlerMap.get(src));
},
-} as Directive;
+} as Directive<HTMLElement>;
diff --git a/packages/frontend/src/directives/anim.ts b/packages/frontend/src/directives/anim.ts
index ad0cb5ed81..a165fa11e0 100644
--- a/packages/frontend/src/directives/anim.ts
+++ b/packages/frontend/src/directives/anim.ts
@@ -5,8 +5,8 @@
import type { Directive } from 'vue';
-export default {
- beforeMount(src, binding, vn) {
+export const animDirective = {
+ beforeMount(src) {
src.style.opacity = '0';
src.style.transform = 'scale(0.9)';
// ページネーションと相性が悪いので
@@ -14,10 +14,10 @@ export default {
src.classList.add('_zoom');
},
- mounted(src, binding, vn) {
+ mounted(src) {
window.setTimeout(() => {
src.style.opacity = '1';
src.style.transform = 'none';
}, 1);
},
-} as Directive;
+} as Directive<HTMLElement>;
diff --git a/packages/frontend/src/directives/appear.ts b/packages/frontend/src/directives/appear.ts
index f5fec108dc..f714871420 100644
--- a/packages/frontend/src/directives/appear.ts
+++ b/packages/frontend/src/directives/appear.ts
@@ -6,12 +6,16 @@
import { throttle } from 'throttle-debounce';
import type { Directive } from 'vue';
-export default {
- mounted(src, binding, vn) {
+interface HTMLElementWithObserver extends HTMLElement {
+ _observer_?: IntersectionObserver;
+}
+
+export const appearDirective = {
+ mounted(src, binding) {
const fn = binding.value;
if (fn == null) return;
- const check = throttle(1000, (entries) => {
+ const check = throttle<IntersectionObserverCallback>(1000, (entries) => {
if (entries.some(entry => entry.isIntersecting)) {
fn();
}
@@ -24,7 +28,7 @@ export default {
src._observer_ = observer;
},
- unmounted(src, binding, vn) {
+ unmounted(src) {
if (src._observer_) src._observer_.disconnect();
},
-} as Directive;
+} as Directive<HTMLElementWithObserver, () => void>;
diff --git a/packages/frontend/src/directives/click-anime.ts b/packages/frontend/src/directives/click-anime.ts
index c34f351fb3..7891e8092c 100644
--- a/packages/frontend/src/directives/click-anime.ts
+++ b/packages/frontend/src/directives/click-anime.ts
@@ -6,8 +6,8 @@
import type { Directive } from 'vue';
import { prefer } from '@/preferences.js';
-export default {
- mounted(el: HTMLElement, binding, vn) {
+export const clickAnimeDirective = {
+ mounted(el) {
if (!prefer.s.animation) return;
const target = el.children[0];
@@ -37,4 +37,4 @@ export default {
target.classList.add('_anime_bounce_standBy');
});
},
-} as Directive;
+} as Directive<HTMLElement>;
diff --git a/packages/frontend/src/directives/follow-append.ts b/packages/frontend/src/directives/follow-append.ts
index f3eaac10e3..303dcb842a 100644
--- a/packages/frontend/src/directives/follow-append.ts
+++ b/packages/frontend/src/directives/follow-append.ts
@@ -6,8 +6,12 @@
import type { Directive } from 'vue';
import { getScrollContainer, getScrollPosition } from '@@/js/scroll.js';
-export default {
- mounted(src, binding, vn) {
+interface HTMLElementWithRO extends HTMLElement {
+ _ro_?: ResizeObserver;
+}
+
+export const followAppendDirective = {
+ mounted(src, binding) {
if (binding.value === false) return;
let isBottom = true;
@@ -34,7 +38,7 @@ export default {
src._ro_ = ro;
},
- unmounted(src, binding, vn) {
+ unmounted(src) {
if (src._ro_) src._ro_.unobserve(src);
},
-} as Directive;
+} as Directive<HTMLElementWithRO, boolean>;
diff --git a/packages/frontend/src/directives/get-size.ts b/packages/frontend/src/directives/get-size.ts
index 488f201a0d..42660987dd 100644
--- a/packages/frontend/src/directives/get-size.ts
+++ b/packages/frontend/src/directives/get-size.ts
@@ -37,8 +37,10 @@ function calc(src: Element) {
info.fn(width, height);
}
-export default {
- mounted(src, binding, vn) {
+type SizeCallback = (w: number, h: number) => void;
+
+export const getSizeDirective = {
+ mounted(src, binding) {
const resize = new ResizeObserver((entries, observer) => {
calc(src);
});
@@ -48,7 +50,7 @@ export default {
calc(src);
},
- unmounted(src, binding, vn) {
+ unmounted(src, binding) {
binding.value(0, 0);
const info = mountings.get(src);
if (!info) return;
@@ -56,4 +58,4 @@ export default {
if (info.intersection) info.intersection.disconnect();
mountings.delete(src);
},
-} as Directive<Element, (w: number, h: number) => void>;
+} as Directive<Element, SizeCallback>;
diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts
index 63637ab2ba..d8fdfe647a 100644
--- a/packages/frontend/src/directives/hotkey.ts
+++ b/packages/frontend/src/directives/hotkey.ts
@@ -5,8 +5,14 @@
import type { Directive } from 'vue';
import { makeHotkey } from '@/utility/hotkey.js';
+import type { Keymap } from '@/utility/hotkey.js';
-export default {
+interface HTMLElementWithHotkey extends HTMLElement {
+ _hotkey_global?: boolean;
+ _keyHandler?: (ev: KeyboardEvent) => void;
+}
+
+export const hotkeyDirective = {
mounted(el, binding) {
el._hotkey_global = binding.modifiers.global === true;
@@ -20,10 +26,11 @@ export default {
},
unmounted(el) {
+ if (el._keyHandler == null) return;
if (el._hotkey_global) {
window.document.removeEventListener('keydown', el._keyHandler);
} else {
el.removeEventListener('keydown', el._keyHandler);
}
},
-} as Directive;
+} as Directive<HTMLElementWithHotkey, Keymap>;
diff --git a/packages/frontend/src/directives/index.ts b/packages/frontend/src/directives/index.ts
index 9555045afe..07b756b95d 100644
--- a/packages/frontend/src/directives/index.ts
+++ b/packages/frontend/src/directives/index.ts
@@ -3,19 +3,19 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import type { App } from 'vue';
+import type { App, Directive } from 'vue';
-import userPreview from './user-preview.js';
-import getSize from './get-size.js';
-import ripple from './ripple.js';
-import tooltip from './tooltip.js';
-import hotkey from './hotkey.js';
-import appear from './appear.js';
-import anim from './anim.js';
-import clickAnime from './click-anime.js';
-import panel from './panel.js';
-import adaptiveBorder from './adaptive-border.js';
-import adaptiveBg from './adaptive-bg.js';
+import { userPreviewDirective } from './user-preview.js';
+import { getSizeDirective } from './get-size.js';
+import { rippleDirective } from './ripple.js';
+import { tooltipDirective } from './tooltip.js';
+import { hotkeyDirective } from './hotkey.js';
+import { appearDirective } from './appear.js';
+import { animDirective } from './anim.js';
+import { clickAnimeDirective } from './click-anime.js';
+import { panelDirective } from './panel.js';
+import { adaptiveBorderDirective } from './adaptive-border.js';
+import { adaptiveBgDirective } from './adaptive-bg.js';
export default function(app: App) {
for (const [key, value] of Object.entries(directives)) {
@@ -24,16 +24,32 @@ export default function(app: App) {
}
export const directives = {
- 'userPreview': userPreview,
- 'user-preview': userPreview,
- 'get-size': getSize,
- 'ripple': ripple,
- 'tooltip': tooltip,
- 'hotkey': hotkey,
- 'appear': appear,
- 'anim': anim,
- 'click-anime': clickAnime,
- 'panel': panel,
- 'adaptive-border': adaptiveBorder,
- 'adaptive-bg': adaptiveBg,
-};
+ 'userPreview': userPreviewDirective,
+ 'user-preview': userPreviewDirective,
+ 'get-size': getSizeDirective,
+ 'ripple': rippleDirective,
+ 'tooltip': tooltipDirective,
+ 'hotkey': hotkeyDirective,
+ 'appear': appearDirective,
+ 'anim': animDirective,
+ 'click-anime': clickAnimeDirective,
+ 'panel': panelDirective,
+ 'adaptive-border': adaptiveBorderDirective,
+ 'adaptive-bg': adaptiveBgDirective,
+} as Record<string, Directive>;
+
+declare module 'vue' {
+ export interface ComponentCustomProperties {
+ vUserPreview: typeof userPreviewDirective;
+ vGetSize: typeof getSizeDirective;
+ vRipple: typeof rippleDirective;
+ vTooltip: typeof tooltipDirective;
+ vHotkey: typeof hotkeyDirective;
+ vAppear: typeof appearDirective;
+ vAnim: typeof animDirective;
+ vClickAnime: typeof clickAnimeDirective;
+ vPanel: typeof panelDirective;
+ vAdaptiveBorder: typeof adaptiveBorderDirective;
+ vAdaptiveBg: typeof adaptiveBgDirective;
+ }
+}
diff --git a/packages/frontend/src/directives/panel.ts b/packages/frontend/src/directives/panel.ts
index 0af19e6ca3..7913baefe4 100644
--- a/packages/frontend/src/directives/panel.ts
+++ b/packages/frontend/src/directives/panel.ts
@@ -6,8 +6,8 @@
import type { Directive } from 'vue';
import { getBgColor } from '@/utility/get-bg-color.js';
-export default {
- mounted(src, binding, vn) {
+export const panelDirective = {
+ mounted(src) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel');
@@ -18,4 +18,4 @@ export default {
src.style.backgroundColor = 'var(--MI_THEME-panel)';
}
},
-} as Directive;
+} as Directive<HTMLElement>;
diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts
index 614cd37011..bacf49ab72 100644
--- a/packages/frontend/src/directives/ripple.ts
+++ b/packages/frontend/src/directives/ripple.ts
@@ -3,12 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import type { Directive } from 'vue';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { prefer } from '@/preferences.js';
import { popup } from '@/os.js';
-export default {
- mounted(el, binding, vn) {
+export const rippleDirective = {
+ mounted(el, binding) {
// 明示的に false であればバインドしない
if (binding.value === false) return;
if (!prefer.s.animation) return;
@@ -24,4 +25,4 @@ export default {
});
});
},
-};
+} as Directive<HTMLElement, boolean | undefined>;
diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts
index 62aecbc87c..9cfa8d657d 100644
--- a/packages/frontend/src/directives/tooltip.ts
+++ b/packages/frontend/src/directives/tooltip.ts
@@ -14,13 +14,30 @@ import { popup, alert } from '@/os.js';
const start = isTouchUsing ? 'touchstart' : 'mouseenter';
const end = isTouchUsing ? 'touchend' : 'mouseleave';
-export default {
- mounted(el: HTMLElement, binding, vn) {
+type TooltipDirectiveState = {
+ text: string;
+ _close: null | (() => void);
+ showTimer: number | null;
+ hideTimer: number | null;
+ checkTimer: number | null;
+ show: () => void;
+ close: () => void;
+};
+
+interface TooltipDirectiveElement extends HTMLElement {
+ _tooltipDirective_?: TooltipDirectiveState;
+}
+
+type TooltipDirectiveModifiers = 'left' | 'right' | 'top' | 'bottom' | 'mfm' | 'noDelay';
+type TooltipDirectiveArg = 'dialog';
+
+export const tooltipDirective = {
+ mounted(el, binding) {
const delay = binding.modifiers.noDelay ? 0 : 100;
- const self = (el as any)._tooltipDirective_ = {} as any;
+ const self = el._tooltipDirective_ = {} as TooltipDirectiveState;
- self.text = binding.value as string;
+ self.text = binding.value;
self._close = null;
self.showTimer = null;
self.hideTimer = null;
@@ -28,7 +45,7 @@ export default {
self.close = () => {
if (self._close) {
- window.clearInterval(self.checkTimer);
+ if (self.checkTimer) window.clearInterval(self.checkTimer);
self._close();
self._close = null;
}
@@ -72,8 +89,8 @@ export default {
});
el.addEventListener(start, (ev) => {
- window.clearTimeout(self.showTimer);
- window.clearTimeout(self.hideTimer);
+ if (self.showTimer) window.clearTimeout(self.showTimer);
+ if (self.hideTimer) window.clearTimeout(self.hideTimer);
if (delay === 0) {
self.show();
} else {
@@ -82,8 +99,8 @@ export default {
}, { passive: true });
el.addEventListener(end, () => {
- window.clearTimeout(self.showTimer);
- window.clearTimeout(self.hideTimer);
+ if (self.showTimer) window.clearTimeout(self.showTimer);
+ if (self.hideTimer) window.clearTimeout(self.hideTimer);
if (delay === 0) {
self.close();
} else {
@@ -92,18 +109,23 @@ export default {
}, { passive: true });
el.addEventListener('click', () => {
- window.clearTimeout(self.showTimer);
+ if (self.showTimer) window.clearTimeout(self.showTimer);
self.close();
});
},
updated(el, binding) {
const self = el._tooltipDirective_;
+ if (self == null) return;
self.text = binding.value as string;
},
- unmounted(el, binding, vn) {
+ unmounted(el) {
const self = el._tooltipDirective_;
- window.clearInterval(self.checkTimer);
+ if (self == null) return;
+ if (self.showTimer) window.clearTimeout(self.showTimer);
+ if (self.hideTimer) window.clearTimeout(self.hideTimer);
+ if (self.checkTimer) window.clearTimeout(self.checkTimer);
+ self.close();
},
-} as Directive;
+} as Directive<TooltipDirectiveElement, string, TooltipDirectiveModifiers, TooltipDirectiveArg>;
diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts
index b11ef8f088..76e345a108 100644
--- a/packages/frontend/src/directives/user-preview.ts
+++ b/packages/frontend/src/directives/user-preview.ts
@@ -5,18 +5,19 @@
import { defineAsyncComponent, ref } from 'vue';
import type { Directive } from 'vue';
+import * as Misskey from 'misskey-js';
import { popup } from '@/os.js';
import { isTouchUsing } from '@/utility/touch.js';
export class UserPreview {
- private el;
- private user;
- private showTimer;
- private hideTimer;
- private checkTimer;
- private promise;
+ private el: HTMLElement;
+ private user: string | Misskey.entities.UserDetailed;
+ private showTimer: number | null = null;
+ private hideTimer: number | null = null;
+ private checkTimer: number | null = null;
+ private promise: null | { cancel: () => void } = null;
- constructor(el, user) {
+ constructor(el: HTMLElement, user: string | Misskey.entities.UserDetailed) {
this.el = el;
this.user = user;
@@ -43,10 +44,10 @@ export class UserPreview {
source: this.el,
}, {
mouseover: () => {
- window.clearTimeout(this.hideTimer);
+ if (this.hideTimer) window.clearTimeout(this.hideTimer);
},
mouseleave: () => {
- window.clearTimeout(this.showTimer);
+ if (this.showTimer) window.clearTimeout(this.showTimer);
this.hideTimer = window.setTimeout(this.close, 500);
},
closed: () => dispose(),
@@ -60,8 +61,8 @@ export class UserPreview {
this.checkTimer = window.setInterval(() => {
if (!window.document.body.contains(this.el)) {
- window.clearTimeout(this.showTimer);
- window.clearTimeout(this.hideTimer);
+ if (this.showTimer) window.clearTimeout(this.showTimer);
+ if (this.hideTimer) window.clearTimeout(this.hideTimer);
this.close();
}
}, 1000);
@@ -69,26 +70,26 @@ export class UserPreview {
private close() {
if (this.promise) {
- window.clearInterval(this.checkTimer);
+ if (this.checkTimer) window.clearInterval(this.checkTimer);
this.promise.cancel();
this.promise = null;
}
}
private onMouseover() {
- window.clearTimeout(this.showTimer);
- window.clearTimeout(this.hideTimer);
+ if (this.showTimer) window.clearTimeout(this.showTimer);
+ if (this.hideTimer) window.clearTimeout(this.hideTimer);
this.showTimer = window.setTimeout(this.show, 500);
}
private onMouseleave() {
- window.clearTimeout(this.showTimer);
- window.clearTimeout(this.hideTimer);
+ if (this.showTimer) window.clearTimeout(this.showTimer);
+ if (this.hideTimer) window.clearTimeout(this.hideTimer);
this.hideTimer = window.setTimeout(this.close, 500);
}
private onClick() {
- window.clearTimeout(this.showTimer);
+ if (this.showTimer) window.clearTimeout(this.showTimer);
this.close();
}
@@ -105,8 +106,14 @@ export class UserPreview {
}
}
-export default {
- mounted(el: HTMLElement, binding, vn) {
+interface UserPreviewDirectiveElement extends HTMLElement {
+ _userPreviewDirective_?: {
+ preview: UserPreview;
+ };
+}
+
+export const userPreviewDirective = {
+ mounted(el, binding) {
if (binding.value == null) return;
if (isTouchUsing) return;
@@ -117,10 +124,11 @@ export default {
self.preview = new UserPreview(el, binding.value);
},
- unmounted(el, binding, vn) {
+ unmounted(el, binding) {
if (binding.value == null) return;
const self = el._userPreviewDirective_;
+ if (self == null) return;
self.preview.detach();
},
-} as Directive;
+} as Directive<UserPreviewDirectiveElement, string | Misskey.entities.UserDetailed>;
diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue
index e3021778e7..c5f3c2d4f0 100644
--- a/packages/frontend/src/pages/admin/performance.vue
+++ b/packages/frontend/src/pages/admin/performance.vue
@@ -54,6 +54,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</SearchMarker>
<SearchMarker>
+ <div class="_panel" style="padding: 16px;">
+ <MkSwitch v-model="showRoleBadgesOfRemoteUsers" @change="onChange_showRoleBadgesOfRemoteUsers">
+ <template #label><SearchLabel>{{ i18n.ts.showRoleBadgesOfRemoteUsers }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
+ </MkSwitch>
+ </div>
+ </SearchMarker>
+
+ <SearchMarker>
<MkFolder :defaultOpen="true">
<template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
<template #label><SearchLabel>Misskey® Fan-out Timeline Technology™ (FTT)</SearchLabel></template>
@@ -188,6 +197,7 @@ const enableIdenticonGeneration = ref(meta.enableIdenticonGeneration);
const enableChartsForRemoteUser = ref(meta.enableChartsForRemoteUser);
const enableStatsForFederatedInstances = ref(meta.enableStatsForFederatedInstances);
const enableChartsForFederatedInstances = ref(meta.enableChartsForFederatedInstances);
+const showRoleBadgesOfRemoteUsers = ref(meta.showRoleBadgesOfRemoteUsers);
function onChange_enableServerMachineStats(value: boolean) {
os.apiWithDialog('admin/update-meta', {
@@ -229,6 +239,14 @@ function onChange_enableChartsForFederatedInstances(value: boolean) {
});
}
+function onChange_showRoleBadgesOfRemoteUsers(value: boolean) {
+ os.apiWithDialog('admin/update-meta', {
+ showRoleBadgesOfRemoteUsers: value,
+ }).then(() => {
+ fetchInstance(true);
+ });
+}
+
const fttForm = useForm({
enableFanoutTimeline: meta.enableFanoutTimeline,
enableFanoutTimelineDbFallback: meta.enableFanoutTimelineDbFallback,
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index 54e214241b..10901f737b 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder v-for="token in items" :key="token.id" :defaultOpen="true">
<template #icon>
<img v-if="token.iconUrl" :class="$style.appIcon" :src="token.iconUrl" alt=""/>
- <i v-else class="ti ti-plug"/>
+ <i v-else class="ti ti-plug"></i>
</template>
<template #label>{{ token.name }}</template>
<template #caption>{{ token.description }}</template>
@@ -86,6 +86,7 @@ definePage(() => ({
<style lang="scss" module>
.appIcon {
+ display: block;
width: 20px;
height: 20px;
border-radius: 4px;
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 3002eddda8..ae7893a7a8 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -368,23 +368,19 @@ onDeactivated(disposeBannerParallaxResizeObserver);
> .banner-container {
position: relative;
- height: 250px;
+ --bannerHeight: 250px;
+ height: var(--bannerHeight);
overflow: clip;
- background-size: cover;
- background-position: center;
> .banner {
- position: absolute;
- top: 50%;
- left: 0;
width: 100%;
- height: 300%;
- background-size: 100% auto;
+ height: 100%;
+ background-size: cover;
background-color: #4c5e6d;
background-repeat: repeat-y;
- background-position: center;
- will-change: transform;
- transform: translateY(-50%);
+ background-position-x: center;
+ background-position-y: 50%;
+ will-change: background-position-y;
}
> .fade {
@@ -677,7 +673,8 @@ onDeactivated(disposeBannerParallaxResizeObserver);
> .main {
> .profile > .main {
> .banner-container {
- height: 140px;
+ --bannerHeight: 140px;
+ height: var(--bannerHeight);
> .fade {
display: none;
@@ -763,10 +760,10 @@ onDeactivated(disposeBannerParallaxResizeObserver);
@keyframes bannerParallaxKeyframes {
from {
- transform: translateY(-50%);
+ background-position-y: 50%;
}
to {
- transform: translateY(-30%);
+ background-position-y: calc(50% + var(--bannerHeight, 250px) / 3);
}
}
</style>
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index e2ee4b658e..ff8e91663a 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -167,7 +167,7 @@ const columnsEl = useTemplateRef('columnsEl');
const addColumn = async (ev) => {
const { canceled, result: column } = await os.select({
title: i18n.ts._deck.addColumn,
- items: columnTypes.map(column => ({
+ items: columnTypes.filter(column => column !== 'chat' || $i == null || $i.policies.chatAvailability !== 'unavailable').map(column => ({
value: column, label: i18n.ts._deck._columns[column],
})),
});
diff --git a/packages/frontend/src/ui/deck/chat-column.vue b/packages/frontend/src/ui/deck/chat-column.vue
index 791af2e44c..0015447e22 100644
--- a/packages/frontend/src/ui/deck/chat-column.vue
+++ b/packages/frontend/src/ui/deck/chat-column.vue
@@ -7,21 +7,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<XColumn :column="column" :isStacked="isStacked">
<template #header><i class="ti ti-messages" style="margin-right: 8px;"></i>{{ column.name || i18n.ts._deck._columns.chat }}</template>
- <div style="padding: 8px;">
- <MkChatHistories/>
+ <div style="padding: 8px;" class="_gaps">
+ <MkInfo v-if="$i.policies.chatAvailability === 'readonly'">{{ i18n.ts._chat.chatIsReadOnlyForThisAccountOrServer }}</MkInfo>
+ <MkInfo v-else-if="$i.policies.chatAvailability === 'unavailable'" warn>{{ i18n.ts._chat.chatNotAvailableForThisAccountOrServer }}</MkInfo>
+ <MkChatHistories v-if="$i.policies.chatAvailability !== 'unavailable'"/>
</div>
</XColumn>
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
+import { ensureSignin } from '@/i.js';
import { i18n } from '../../i18n.js';
import XColumn from './column.vue';
import type { Column } from '@/deck.js';
+import MkInfo from '@/components/MkInfo.vue';
import MkChatHistories from '@/components/MkChatHistories.vue';
defineProps<{
column: Column;
isStacked: boolean;
}>();
+
+const $i = ensureSignin();
</script>
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1757e6861e..8c395d527e 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
- "version": "2025.10.1-alpha.2",
+ "version": "2025.10.1-alpha.3",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 7edd43bf9b..3e95651071 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -9460,6 +9460,7 @@ export interface operations {
enableRemoteNotesCleaning: boolean;
remoteNotesCleaningExpiryDaysForEachNotes: number;
remoteNotesCleaningMaxProcessingDurationInMinutes: number;
+ showRoleBadgesOfRemoteUsers: boolean;
};
};
};
@@ -12780,6 +12781,7 @@ export interface operations {
enableRemoteNotesCleaning?: boolean;
remoteNotesCleaningExpiryDaysForEachNotes?: number;
remoteNotesCleaningMaxProcessingDurationInMinutes?: number;
+ showRoleBadgesOfRemoteUsers?: boolean;
};
};
};
diff --git a/patches/.gitkeep b/patches/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/patches/.gitkeep
diff --git a/patches/typeorm.patch b/patches/typeorm.patch
deleted file mode 100644
index 6b30cddb66..0000000000
--- a/patches/typeorm.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-diff --git a/driver/postgres/PostgresDriver.js b/driver/postgres/PostgresDriver.js
-index e13b903c73b71113bb529552e59fb4ce0ca8af0c..50de6a60120ece7ebf49009eac588a5313343f39 100644
---- a/driver/postgres/PostgresDriver.js
-+++ b/driver/postgres/PostgresDriver.js
-@@ -819,10 +819,10 @@ class PostgresDriver {
- const tableColumnDefault = typeof tableColumn.default === "string"
- ? JSON.parse(tableColumn.default.substring(1, tableColumn.default.length - 1))
- : tableColumn.default;
-- return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault);
-+ return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault ?? null);
- }
- const columnDefault = this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata));
-- return columnDefault === tableColumn.default;
-+ return columnDefault === tableColumn.default || columnDefault === undefined && tableColumn.default.toLowerCase() === 'null';
- }
- /**
- * Normalizes "isUnique" value of the column.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3fc2f76c70..689250d4e2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,11 +9,6 @@ overrides:
lodash: 4.17.21
'@aiscript-dev/aiscript-languageserver': '-'
-patchedDependencies:
- typeorm:
- hash: b1141d7fd5ff7abdae28a93b2a634f595c7848518643c08d120698da1d2b4ebc
- path: patches/typeorm.patch
-
importers:
.:
@@ -430,7 +425,7 @@ importers:
version: 4.2.0
typeorm:
specifier: 0.3.27
- version: 0.3.27(patch_hash=b1141d7fd5ff7abdae28a93b2a634f595c7848518643c08d120698da1d2b4ebc)(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2)
+ version: 0.3.27(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2)
typescript:
specifier: 5.9.3
version: 5.9.3
@@ -22133,7 +22128,7 @@ snapshots:
typedarray@0.0.6: {}
- typeorm@0.3.27(patch_hash=b1141d7fd5ff7abdae28a93b2a634f595c7848518643c08d120698da1d2b4ebc)(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2):
+ typeorm@0.3.27(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2):
dependencies:
'@sqltools/formatter': 1.2.5
ansis: 3.17.0