diff --git a/package.json b/package.json index 55286bc5a7..28441a7bea 100644 --- a/package.json +++ b/package.json @@ -85,9 +85,6 @@ "pnpm": { "overrides": { "@aiscript-dev/aiscript-languageserver": "-" - }, - "patchedDependencies": { - "typeorm": "patches/typeorm.patch" } } } 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/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; 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(); -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; 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; 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(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 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; 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; 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 void>; +} as Directive; 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; 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; + +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; 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; 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; 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; diff --git a/patches/.gitkeep b/patches/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 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