diff options
| author | zyoshoka <107108195+zyoshoka@users.noreply.github.com> | 2023-12-07 14:42:09 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-07 14:42:09 +0900 |
| commit | 406b4bdbe79b5b0b68fcdcb3c4b6e419460a0258 (patch) | |
| tree | a1af1cc6102d2db40a687bc848c07cce35bd414f /packages/frontend/src/components | |
| parent | feat: Roleに関するSchemaを追加 (#12572) (diff) | |
| download | sharkey-406b4bdbe79b5b0b68fcdcb3c4b6e419460a0258.tar.gz sharkey-406b4bdbe79b5b0b68fcdcb3c4b6e419460a0258.tar.bz2 sharkey-406b4bdbe79b5b0b68fcdcb3c4b6e419460a0258.zip | |
refactor(frontend): 非推奨となったReactivity Transformを使わないように (#12539)
* refactor(frontend): 非推奨となったReactivity Transformを使わないように
* refactor: 不要な括弧を除去
* fix: 不要なアノテーションを除去
* fix: Refの配列をrefしている部分の対応
* refactor: 不要な括弧を除去
* fix: lint
* refactor: Ref、ShallowRef、ComputedRefの変数の宣言をletからconstに置換
* fix: type error
* chore: drop reactivity transform from eslint configuration
* refactor: remove unnecessary import
* fix: 対応漏れ
Diffstat (limited to 'packages/frontend/src/components')
80 files changed, 1122 insertions, 1119 deletions
diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index 2c7be319cb..ce7e134b70 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { ref } from 'vue'; import MkButton from '@/components/MkButton.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; @@ -56,11 +57,11 @@ const emit = defineEmits<{ (ev: 'resolved', reportId: string): void; }>(); -let forward = $ref(props.report.forwarded); +const forward = ref(props.report.forwarded); function resolve() { os.apiWithDialog('admin/resolve-abuse-user-report', { - forward: forward, + forward: forward.value, reportId: props.report.id, }).then(() => { emit('resolved', props.report.id); diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index bea0ed26d8..b067e94a6d 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import * as Misskey from 'misskey-js'; -import { onMounted } from 'vue'; +import { onMounted, ref, computed } from 'vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js'; @@ -67,15 +67,15 @@ const props = withDefaults(defineProps<{ withDescription: true, }); -let achievements = $ref(); -const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x))); +const achievements = ref(); +const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x))); function fetch() { os.api('users/achievements', { userId: props.user.id }).then(res => { - achievements = []; + achievements.value = []; for (const t of ACHIEVEMENT_TYPES) { const a = res.find(x => x.name === t); - if (a) achievements.push(a); + if (a) achievements.value.push(a); } //achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt); }); diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue index cd2c4d8264..0e252f7b1d 100644 --- a/packages/frontend/src/components/MkAnalogClock.vue +++ b/packages/frontend/src/components/MkAnalogClock.vue @@ -138,45 +138,45 @@ const texts = computed(() => { }); let enabled = true; -let majorGraduationColor = $ref<string>(); +const majorGraduationColor = ref<string>(); //let minorGraduationColor = $ref<string>(); -let sHandColor = $ref<string>(); -let mHandColor = $ref<string>(); -let hHandColor = $ref<string>(); -let nowColor = $ref<string>(); -let h = $ref<number>(0); -let m = $ref<number>(0); -let s = $ref<number>(0); -let hAngle = $ref<number>(0); -let mAngle = $ref<number>(0); -let sAngle = $ref<number>(0); -let disableSAnimate = $ref(false); +const sHandColor = ref<string>(); +const mHandColor = ref<string>(); +const hHandColor = ref<string>(); +const nowColor = ref<string>(); +const h = ref<number>(0); +const m = ref<number>(0); +const s = ref<number>(0); +const hAngle = ref<number>(0); +const mAngle = ref<number>(0); +const sAngle = ref<number>(0); +const disableSAnimate = ref(false); let sOneRound = false; const sLine = ref<SVGPathElement>(); function tick() { const now = props.now(); now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset); - const previousS = s; - const previousM = m; - const previousH = h; - s = now.getSeconds(); - m = now.getMinutes(); - h = now.getHours(); - if (previousS === s && previousM === m && previousH === h) { + const previousS = s.value; + const previousM = m.value; + const previousH = h.value; + s.value = now.getSeconds(); + m.value = now.getMinutes(); + h.value = now.getHours(); + if (previousS === s.value && previousM === m.value && previousH === h.value) { return; } - hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6); - mAngle = Math.PI * (m + s / 60) / 30; + hAngle.value = Math.PI * (h.value % (props.twentyfour ? 24 : 12) + (m.value + s.value / 60) / 60) / (props.twentyfour ? 12 : 6); + mAngle.value = Math.PI * (m.value + s.value / 60) / 30; if (sOneRound && sLine.value) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない) - sAngle = Math.PI * 60 / 30; + sAngle.value = Math.PI * 60 / 30; defaultIdlingRenderScheduler.delete(tick); sLine.value.addEventListener('transitionend', () => { - disableSAnimate = true; + disableSAnimate.value = true; requestAnimationFrame(() => { - sAngle = 0; + sAngle.value = 0; requestAnimationFrame(() => { - disableSAnimate = false; + disableSAnimate.value = false; if (enabled) { defaultIdlingRenderScheduler.add(tick); } @@ -184,9 +184,9 @@ function tick() { }); }, { once: true }); } else { - sAngle = Math.PI * s / 30; + sAngle.value = Math.PI * s.value / 30; } - sOneRound = s === 59; + sOneRound = s.value === 59; } tick(); @@ -195,12 +195,12 @@ function calcColors() { const computedStyle = getComputedStyle(document.documentElement); const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark(); const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString(); - majorGraduationColor = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)'; + majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)'; //minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; - sHandColor = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)'; - mHandColor = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString(); - hHandColor = accent; - nowColor = accent; + sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)'; + mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString(); + hHandColor.value = accent; + nowColor.value = accent; } calcColors(); diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue index 099baf0d72..7670b54f16 100644 --- a/packages/frontend/src/components/MkAsUi.vue +++ b/packages/frontend/src/components/MkAsUi.vue @@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { Ref } from 'vue'; +import { Ref, ref } from 'vue'; import * as os from '@/os.js'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -87,10 +87,10 @@ function g(id) { return props.components.find(x => x.value.id === id).value; } -let valueForSwitch = $ref(c.default ?? false); +const valueForSwitch = ref(c.default ?? false); function onSwitchUpdate(v) { - valueForSwitch = v; + valueForSwitch.value = v; if (c.onChange) c.onChange(v); } diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index bcd58ae516..8b176eedaa 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { nextTick, onMounted } from 'vue'; +import { nextTick, onMounted, shallowRef } from 'vue'; const props = defineProps<{ type?: 'button' | 'submit' | 'reset'; @@ -59,13 +59,13 @@ const emit = defineEmits<{ (ev: 'click', payload: MouseEvent): void; }>(); -let el = $shallowRef<HTMLElement | null>(null); -let ripples = $shallowRef<HTMLElement | null>(null); +const el = shallowRef<HTMLElement | null>(null); +const ripples = shallowRef<HTMLElement | null>(null); onMounted(() => { if (props.autofocus) { nextTick(() => { - el!.focus(); + el.value!.focus(); }); } }); @@ -88,11 +88,11 @@ function onMousedown(evt: MouseEvent): void { const rect = target.getBoundingClientRect(); const ripple = document.createElement('div'); - ripple.classList.add(ripples!.dataset.childrenClass!); + ripple.classList.add(ripples.value!.dataset.childrenClass!); ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px'; ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px'; - ripples!.appendChild(ripple); + ripples.value!.appendChild(ripple); const circleCenterX = evt.clientX - rect.left; const circleCenterY = evt.clientY - rect.top; @@ -107,7 +107,7 @@ function onMousedown(evt: MouseEvent): void { ripple.style.opacity = '0'; }, 1000); window.setTimeout(() => { - if (ripples) ripples.removeChild(ripple); + if (ripples.value) ripples.value.removeChild(ripple); }, 2000); } </script> diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index fe7077bdbf..adb3c134ae 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -74,7 +74,7 @@ const props = defineProps({ }, }); -let legendEl = $shallowRef<InstanceType<typeof MkChartLegend>>(); +const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>(); const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b)); const negate = arr => arr.map(x => -x); @@ -268,7 +268,7 @@ const render = () => { gradient, }, }, - plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl)] : [])], + plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl.value)] : [])], }); }; diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue index d321114cba..1a1b4323d9 100644 --- a/packages/frontend/src/components/MkChartLegend.vue +++ b/packages/frontend/src/components/MkChartLegend.vue @@ -13,29 +13,30 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { shallowRef } from 'vue'; import { Chart, LegendItem } from 'chart.js'; const props = defineProps({ }); -let chart = $shallowRef<Chart>(); -let items = $shallowRef<LegendItem[]>([]); +const chart = shallowRef<Chart>(); +const items = shallowRef<LegendItem[]>([]); function update(_chart: Chart, _items: LegendItem[]) { - chart = _chart, - items = _items; + chart.value = _chart, + items.value = _items; } function onClick(item: LegendItem) { - if (chart == null) return; - const { type } = chart.config; + if (chart.value == null) return; + const { type } = chart.value.config; if (type === 'pie' || type === 'doughnut') { // Pie and doughnut charts only have a single dataset and visibility is per item - chart.toggleDataVisibility(item.index); + chart.value.toggleDataVisibility(item.index); } else { - chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex)); + chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex)); } - chart.update(); + chart.value.update(); } defineExpose({ diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 1c3920962e..f255961e25 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, onMounted, onUnmounted } from 'vue'; +import { computed, onMounted, onUnmounted, ref } from 'vue'; import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue'; import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; @@ -29,8 +29,8 @@ import { claimAchievement } from '@/scripts/achievements.js'; const saveData = game.saveData; const cookies = computed(() => saveData.value?.cookies); -let cps = $ref(0); -let prevCookies = $ref(0); +const cps = ref(0); +const prevCookies = ref(0); function onClick(ev: MouseEvent) { const x = ev.clientX; @@ -48,9 +48,9 @@ function onClick(ev: MouseEvent) { } useInterval(() => { - const diff = saveData.value!.cookies - prevCookies; - cps = diff; - prevCookies = saveData.value!.cookies; + const diff = saveData.value!.cookies - prevCookies.value; + cps.value = diff; + prevCookies.value = saveData.value!.cookies; }, 1000, { immediate: false, afterMounted: true, @@ -63,7 +63,7 @@ useInterval(game.save, 1000 * 5, { onMounted(async () => { await game.load(); - prevCookies = saveData.value!.cookies; + prevCookies.value = saveData.value!.cookies; }); onUnmounted(() => { diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue index 6cca7fc353..b78252be89 100644 --- a/packages/frontend/src/components/MkContextMenu.vue +++ b/packages/frontend/src/components/MkContextMenu.vue @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onBeforeUnmount } from 'vue'; +import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue'; import MkMenu from './MkMenu.vue'; import { MenuItem } from './types/menu.vue'; import contains from '@/scripts/contains.js'; @@ -34,9 +34,9 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -let rootEl = $shallowRef<HTMLDivElement>(); +const rootEl = shallowRef<HTMLDivElement>(); -let zIndex = $ref<number>(os.claimZIndex('high')); +const zIndex = ref<number>(os.claimZIndex('high')); const SCROLLBAR_THICKNESS = 16; @@ -44,8 +44,8 @@ onMounted(() => { let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1 let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1 - const width = rootEl.offsetWidth; - const height = rootEl.offsetHeight; + const width = rootEl.value.offsetWidth; + const height = rootEl.value.offsetHeight; if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) { left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset; @@ -63,8 +63,8 @@ onMounted(() => { left = 0; } - rootEl.style.top = `${top}px`; - rootEl.style.left = `${left}px`; + rootEl.value.style.top = `${top}px`; + rootEl.value.style.left = `${left}px`; document.body.addEventListener('mousedown', onMousedown); }); @@ -74,7 +74,7 @@ onBeforeUnmount(() => { }); function onMousedown(evt: Event) { - if (!contains(rootEl, evt.target) && (rootEl !== evt.target)) emit('closed'); + if (!contains(rootEl.value, evt.target) && (rootEl.value !== evt.target)) emit('closed'); } </script> diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index 81f3936600..0a1ddd3171 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, shallowRef, ref } from 'vue'; import * as Misskey from 'misskey-js'; import Cropper from 'cropperjs'; import tinycolor from 'tinycolor2'; @@ -56,10 +56,10 @@ const props = defineProps<{ }>(); const imgUrl = getProxiedImageUrl(props.file.url, undefined, true); -let dialogEl = $shallowRef<InstanceType<typeof MkModalWindow>>(); -let imgEl = $shallowRef<HTMLImageElement>(); +const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); +const imgEl = shallowRef<HTMLImageElement>(); let cropper: Cropper | null = null; -let loading = $ref(true); +const loading = ref(true); const ok = async () => { const promise = new Promise<Misskey.entities.DriveFile>(async (res) => { @@ -94,16 +94,16 @@ const ok = async () => { const f = await promise; emit('ok', f); - dialogEl!.close(); + dialogEl.value!.close(); }; const cancel = () => { emit('cancel'); - dialogEl!.close(); + dialogEl.value!.close(); }; const onImageLoad = () => { - loading = false; + loading.value = false; if (cropper) { cropper.getCropperImage()!.$center('contain'); @@ -112,7 +112,7 @@ const onImageLoad = () => { }; onMounted(() => { - cropper = new Cropper(imgEl!, { + cropper = new Cropper(imgEl.value!, { }); const computedStyle = getComputedStyle(document.documentElement); diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 33757ccc83..3c1f83d335 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown"> <template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template> <template #caption> - <span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/> - <span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/> + <span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/> + <span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/> </template> </MkInput> <MkSelect v-if="select" v-model="selectedValue" autofocus> @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkSelect> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> - <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> + <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> <MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> </div> <div v-if="actions" :class="$style.buttons"> @@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue'; +import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from 'vue'; import MkModal from '@/components/MkModal.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -122,24 +122,21 @@ const modal = shallowRef<InstanceType<typeof MkModal>>(); const inputValue = ref<string | number | null>(props.input?.default ?? null); const selectedValue = ref(props.select?.default ?? null); -let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null); -const okButtonDisabled = $computed<boolean>(() => { +const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => { if (props.input) { if (props.input.minLength) { if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) { - disabledReason = 'charactersBelow'; - return true; + return 'charactersBelow'; } } if (props.input.maxLength) { if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) { - disabledReason = 'charactersExceeded'; - return true; + return 'charactersExceeded'; } } } - return false; + return null; }); function done(canceled: boolean, result?) { diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue index 05b137e335..2cce1f5520 100644 --- a/packages/frontend/src/components/MkEmojiPickerDialog.vue +++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue @@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { shallowRef } from 'vue'; import MkModal from '@/components/MkModal.vue'; import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; import { defaultStore } from '@/store.js'; @@ -54,23 +55,23 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const modal = $shallowRef<InstanceType<typeof MkModal>>(); -const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>(); +const modal = shallowRef<InstanceType<typeof MkModal>>(); +const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>(); function chosen(emoji: any) { emit('done', emoji); if (props.choseAndClose) { - modal?.close(); + modal.value?.close(); } } function opening() { - picker?.reset(); - picker?.focus(); + picker.value?.reset(); + picker.value?.focus(); // 何故かちょっと待たないとフォーカスされない setTimeout(() => { - picker?.focus(); + picker.value?.focus(); }, 10); } </script> diff --git a/packages/frontend/src/components/MkFileCaptionEditWindow.vue b/packages/frontend/src/components/MkFileCaptionEditWindow.vue index 28888fb9c8..922089a78b 100644 --- a/packages/frontend/src/components/MkFileCaptionEditWindow.vue +++ b/packages/frontend/src/components/MkFileCaptionEditWindow.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { shallowRef, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkTextarea from '@/components/MkTextarea.vue'; @@ -42,12 +42,12 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); -let caption = $ref(props.default); +const caption = ref(props.default); async function ok() { - emit('done', caption); - dialog.close(); + emit('done', caption.value); + dialog.value.close(); } </script> diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index 60ecc13056..6b7dfb20e3 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { nextTick, onMounted } from 'vue'; +import { nextTick, onMounted, shallowRef, ref } from 'vue'; import { defaultStore } from '@/store.js'; const props = withDefaults(defineProps<{ @@ -70,10 +70,10 @@ const getBgColor = (el: HTMLElement) => { } }; -let rootEl = $shallowRef<HTMLElement>(); -let bgSame = $ref(false); -let opened = $ref(props.defaultOpen); -let openedAtLeastOnce = $ref(props.defaultOpen); +const rootEl = shallowRef<HTMLElement>(); +const bgSame = ref(false); +const opened = ref(props.defaultOpen); +const openedAtLeastOnce = ref(props.defaultOpen); function enter(el) { const elementHeight = el.getBoundingClientRect().height; @@ -98,20 +98,20 @@ function afterLeave(el) { } function toggle() { - if (!opened) { - openedAtLeastOnce = true; + if (!opened.value) { + openedAtLeastOnce.value = true; } nextTick(() => { - opened = !opened; + opened.value = !opened.value; }); } onMounted(() => { const computedStyle = getComputedStyle(document.documentElement); - const parentBg = getBgColor(rootEl.parentElement); + const parentBg = getBgColor(rootEl.value.parentElement); const myBg = computedStyle.getPropertyValue('--panel'); - bgSame = parentBg === myBg; + bgSame.value = parentBg === myBg; }); </script> diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index b8de71e3b7..88d3188189 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onBeforeUnmount, onMounted } from 'vue'; +import { onBeforeUnmount, onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; import { useStream } from '@/stream.js'; @@ -57,9 +57,9 @@ const emit = defineEmits<{ (_: 'update:user', value: Misskey.entities.UserDetailed): void }>(); -let isFollowing = $ref(props.user.isFollowing); -let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou); -let wait = $ref(false); +const isFollowing = ref(props.user.isFollowing); +const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou); +const wait = ref(false); const connection = useStream().useChannel('main'); if (props.user.isFollowing == null) { @@ -71,16 +71,16 @@ if (props.user.isFollowing == null) { function onFollowChange(user: Misskey.entities.UserDetailed) { if (user.id === props.user.id) { - isFollowing = user.isFollowing; - hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou; + isFollowing.value = user.isFollowing; + hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou; } } async function onClick() { - wait = true; + wait.value = true; try { - if (isFollowing) { + if (isFollowing.value) { const { canceled } = await os.confirm({ type: 'warning', text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }), @@ -92,11 +92,11 @@ async function onClick() { userId: props.user.id, }); } else { - if (hasPendingFollowRequestFromYou) { + if (hasPendingFollowRequestFromYou.value) { await os.api('following/requests/cancel', { userId: props.user.id, }); - hasPendingFollowRequestFromYou = false; + hasPendingFollowRequestFromYou.value = false; } else { await os.api('following/create', { userId: props.user.id, @@ -106,7 +106,7 @@ async function onClick() { ...props.user, withReplies: defaultStore.state.defaultWithReplies }); - hasPendingFollowRequestFromYou = true; + hasPendingFollowRequestFromYou.value = true; claimAchievement('following1'); @@ -127,7 +127,7 @@ async function onClick() { } catch (err) { console.error(err); } finally { - wait = false; + wait.value = false; } } diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue index 521ac11d12..9b57688a02 100644 --- a/packages/frontend/src/components/MkForgotPassword.vue +++ b/packages/frontend/src/components/MkForgotPassword.vue @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref } from 'vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -53,19 +53,19 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -let dialog: InstanceType<typeof MkModalWindow> = $ref(); +const dialog = ref<InstanceType<typeof MkModalWindow>>(); -let username = $ref(''); -let email = $ref(''); -let processing = $ref(false); +const username = ref(''); +const email = ref(''); +const processing = ref(false); async function onSubmit() { - processing = true; + processing.value = true; await os.apiWithDialog('request-reset-password', { - username, - email, + username: username.value, + email: email.value, }); emit('done'); - dialog.close(); + dialog.value.close(); } </script> diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue index 0022531e58..a57e6c9292 100644 --- a/packages/frontend/src/components/MkHeatmap.vue +++ b/packages/frontend/src/components/MkHeatmap.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, nextTick, watch } from 'vue'; +import { onMounted, nextTick, watch, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; @@ -27,11 +27,11 @@ const props = defineProps<{ src: string; }>(); -const rootEl = $shallowRef<HTMLDivElement>(null); -const chartEl = $shallowRef<HTMLCanvasElement>(null); +const rootEl = shallowRef<HTMLDivElement>(null); +const chartEl = shallowRef<HTMLCanvasElement>(null); const now = new Date(); let chartInstance: Chart = null; -let fetching = $ref(true); +const fetching = ref(true); const { handler: externalTooltipHandler } = useChartTooltip({ position: 'middle', @@ -42,8 +42,8 @@ async function renderChart() { chartInstance.destroy(); } - const wide = rootEl.offsetWidth > 700; - const narrow = rootEl.offsetWidth < 400; + const wide = rootEl.value.offsetWidth > 700; + const narrow = rootEl.value.offsetWidth < 400; const weeks = wide ? 50 : narrow ? 10 : 25; const chartLimit = 7 * weeks; @@ -88,7 +88,7 @@ async function renderChart() { values = raw.deliverFailed; } - fetching = false; + fetching.value = false; await nextTick(); @@ -101,7 +101,7 @@ async function renderChart() { const marginEachCell = 4; - chartInstance = new Chart(chartEl, { + chartInstance = new Chart(chartEl.value, { type: 'matrix', data: { datasets: [{ @@ -210,7 +210,7 @@ async function renderChart() { } watch(() => props.src, () => { - fetching = true; + fetching.value = true; renderChart(); }); diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue index 4fb573fdbc..942861e1f4 100644 --- a/packages/frontend/src/components/MkImgWithBlurhash.vue +++ b/packages/frontend/src/components/MkImgWithBlurhash.vue @@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts"> -import { $ref } from 'vue/macros'; import DrawBlurhash from '@/workers/draw-blurhash?worker'; import TestWebGL2 from '@/workers/test-webgl2?worker'; import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js'; @@ -58,7 +57,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol </script> <script lang="ts" setup> -import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue'; +import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch, ref } from 'vue'; import { v4 as uuid } from 'uuid'; import { render } from 'buraha'; import { defaultStore } from '@/store.js'; @@ -98,41 +97,41 @@ const viewId = uuid(); const canvas = shallowRef<HTMLCanvasElement>(); const root = shallowRef<HTMLDivElement>(); const img = shallowRef<HTMLImageElement>(); -let loaded = $ref(false); -let canvasWidth = $ref(64); -let canvasHeight = $ref(64); -let imgWidth = $ref(props.width); -let imgHeight = $ref(props.height); -let bitmapTmp = $ref<CanvasImageSource | undefined>(); -const hide = computed(() => !loaded || props.forceBlurhash); +const loaded = ref(false); +const canvasWidth = ref(64); +const canvasHeight = ref(64); +const imgWidth = ref(props.width); +const imgHeight = ref(props.height); +const bitmapTmp = ref<CanvasImageSource | undefined>(); +const hide = computed(() => !loaded.value || props.forceBlurhash); function waitForDecode() { if (props.src != null && props.src !== '') { nextTick() .then(() => img.value?.decode()) .then(() => { - loaded = true; + loaded.value = true; }, error => { console.log('Error occurred during decoding image', img.value, error); }); } else { - loaded = false; + loaded.value = false; } } watch([() => props.width, () => props.height, root], () => { const ratio = props.width / props.height; if (ratio > 1) { - canvasWidth = Math.round(64 * ratio); - canvasHeight = 64; + canvasWidth.value = Math.round(64 * ratio); + canvasHeight.value = 64; } else { - canvasWidth = 64; - canvasHeight = Math.round(64 / ratio); + canvasWidth.value = 64; + canvasHeight.value = Math.round(64 / ratio); } const clientWidth = root.value?.clientWidth ?? 300; - imgWidth = clientWidth; - imgHeight = Math.round(clientWidth / ratio); + imgWidth.value = clientWidth; + imgHeight.value = Math.round(clientWidth / ratio); }, { immediate: true, }); @@ -140,15 +139,15 @@ watch([() => props.width, () => props.height, root], () => { function drawImage(bitmap: CanvasImageSource) { // canvasがない(mountedされていない)場合はTmpに保存しておく if (!canvas.value) { - bitmapTmp = bitmap; + bitmapTmp.value = bitmap; return; } // canvasがあれば描画する - bitmapTmp = undefined; + bitmapTmp.value = undefined; const ctx = canvas.value.getContext('2d'); if (!ctx) return; - ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight); + ctx.drawImage(bitmap, 0, 0, canvasWidth.value, canvasHeight.value); } function drawAvg() { @@ -160,7 +159,7 @@ function drawAvg() { // avgColorでお茶をにごす ctx.beginPath(); ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888'; - ctx.fillRect(0, 0, canvasWidth, canvasHeight); + ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value); } async function draw() { @@ -212,8 +211,8 @@ watch(() => props.hash, () => { onMounted(() => { // drawImageがmountedより先に呼ばれている場合はここで描画する - if (bitmapTmp) { - drawImage(bitmapTmp); + if (bitmapTmp.value) { + drawImage(bitmapTmp.value); } waitForDecode(); }); diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index 9710f779d5..8a63e0cced 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkMiniChart from '@/components/MkMiniChart.vue'; import * as os from '@/os.js'; @@ -24,12 +25,12 @@ const props = defineProps<{ instance: Misskey.entities.FederationInstance; }>(); -let chartValues = $ref<number[] | null>(null); +const chartValues = ref<number[] | null>(null); os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く res['requests.received'].splice(0, 1); - chartValues = res['requests.received']; + chartValues.value = res['requests.received']; }); function getInstanceIcon(instance): string { diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue index 509254de74..7b763ad385 100644 --- a/packages/frontend/src/components/MkInstanceStats.vue +++ b/packages/frontend/src/components/MkInstanceStats.vue @@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, ref, shallowRef } from 'vue'; import { Chart } from 'chart.js'; import MkSelect from '@/components/MkSelect.vue'; import MkChart from '@/components/MkChart.vue'; @@ -100,11 +100,11 @@ import { initChart } from '@/scripts/init-chart.js'; initChart(); const chartLimit = 500; -let chartSpan = $ref<'hour' | 'day'>('hour'); -let chartSrc = $ref('active-users'); -let heatmapSrc = $ref('active-users'); -let subDoughnutEl = $shallowRef<HTMLCanvasElement>(); -let pubDoughnutEl = $shallowRef<HTMLCanvasElement>(); +const chartSpan = ref<'hour' | 'day'>('hour'); +const chartSrc = ref('active-users'); +const heatmapSrc = ref('active-users'); +const subDoughnutEl = shallowRef<HTMLCanvasElement>(); +const pubDoughnutEl = shallowRef<HTMLCanvasElement>(); const { handler: externalTooltipHandler1 } = useChartTooltip({ position: 'middle', @@ -163,7 +163,7 @@ function createDoughnut(chartEl, tooltip, data) { onMounted(() => { os.apiGet('federation/stats', { limit: 30 }).then(fedStats => { - createDoughnut(subDoughnutEl, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({ + createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({ name: x.host, color: x.themeColor, value: x.followersCount, @@ -172,7 +172,7 @@ onMounted(() => { }, })).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }])); - createDoughnut(pubDoughnutEl, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({ + createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({ name: x.host, color: x.themeColor, value: x.followingCount, diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue index 1a4f2bfbb9..3ee2aa7174 100644 --- a/packages/frontend/src/components/MkInstanceTicker.vue +++ b/packages/frontend/src/components/MkInstanceTicker.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { computed } from 'vue'; import { instanceName } from '@/config.js'; import { instance as Instance } from '@/instance.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; @@ -30,7 +30,7 @@ const instance = props.instance ?? { themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content, }; -const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico'); +const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico'); const themeColor = instance.themeColor ?? '#777777'; diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index b16c05f575..120ed7a86c 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { shallowRef } from 'vue'; import MkModal from '@/components/MkModal.vue'; import { navbarItemDef } from '@/navbar.js'; import { defaultStore } from '@/store.js'; @@ -48,7 +48,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop deviceKind === 'smartphone' ? 'drawer' : 'dialog'; -const modal = $shallowRef<InstanceType<typeof MkModal>>(); +const modal = shallowRef<InstanceType<typeof MkModal>>(); const menu = defaultStore.state.menu; @@ -63,7 +63,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k => })); function close() { - modal.close(); + modal.value.close(); } </script> diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue index 0501d797f8..808a071d10 100644 --- a/packages/frontend/src/components/MkLink.vue +++ b/packages/frontend/src/components/MkLink.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent } from 'vue'; +import { defineAsyncComponent, ref } from 'vue'; import { url as local } from '@/config.js'; import { useTooltip } from '@/scripts/use-tooltip.js'; import * as os from '@/os.js'; @@ -29,13 +29,13 @@ const self = props.url.startsWith(local); const attr = self ? 'to' : 'href'; const target = self ? null : '_blank'; -const el = $ref(); +const el = ref(); -useTooltip($$(el), (showing) => { +useTooltip(el, (showing) => { os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), { showing, url: props.url, - source: el, + source: el.value, }, {}, 'closed'); }); </script> diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue index 122f8ad794..92b5388c34 100644 --- a/packages/frontend/src/components/MkMediaBanner.vue +++ b/packages/frontend/src/components/MkMediaBanner.vue @@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, shallowRef, watch } from 'vue'; +import { onMounted, shallowRef, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; @@ -42,7 +42,7 @@ const props = withDefaults(defineProps<{ }); const audioEl = shallowRef<HTMLAudioElement>(); -let hide = $ref(true); +const hide = ref(true); watch(audioEl, () => { if (audioEl.value) { diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue index 003c0979c7..d236b222aa 100644 --- a/packages/frontend/src/components/MkMediaImage.vue +++ b/packages/frontend/src/components/MkMediaImage.vue @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch } from 'vue'; +import { watch, ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import bytes from '@/filters/bytes.js'; @@ -73,10 +73,10 @@ const props = withDefaults(defineProps<{ controls: true, }); -let hide = $ref(true); -let darkMode: boolean = $ref(defaultStore.state.darkMode); +const hide = ref(true); +const darkMode = ref<boolean>(defaultStore.state.darkMode); -const url = $computed(() => (props.raw || defaultStore.state.loadRawImages) +const url = computed(() => (props.raw || defaultStore.state.loadRawImages) ? props.image.url : defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(props.image.url) @@ -87,14 +87,14 @@ function onclick() { if (!props.controls) { return; } - if (hide) { - hide = false; + if (hide.value) { + hide.value = false; } } // Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする watch(() => props.image, () => { - hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore'); + hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore'); }, { deep: true, immediate: true, @@ -105,7 +105,7 @@ function showMenu(ev: MouseEvent) { text: i18n.ts.hide, icon: 'ti ti-eye-off', action: () => { - hide = true; + hide.value = true; }, }, ...(iAmModerator ? [{ text: i18n.ts.markAsSensitive, @@ -126,7 +126,7 @@ function showMenu(ev: MouseEvent) { .sensitive { position: relative; - + &::after { content: ""; position: absolute; diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index f134f2021d..b154eb0202 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -63,7 +63,7 @@ async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLE </script> <script lang="ts" setup> -import { onMounted, onUnmounted, shallowRef } from 'vue'; +import { computed, onMounted, onUnmounted, shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import PhotoSwipeLightbox from 'photoswipe/lightbox'; import PhotoSwipe from 'photoswipe'; @@ -86,7 +86,7 @@ const container = shallowRef<HTMLElement | null | undefined>(undefined); const gallery = shallowRef<HTMLDivElement>(); const pswpZIndex = os.claimZIndex('middle'); document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); -const count = $computed(() => props.mediaList.filter(media => previewable(media)).length); +const count = computed(() => props.mediaList.filter(media => previewable(media)).length); let lightbox: PhotoSwipeLightbox | null; const popstateHandler = (): void => { diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 736f48ea3c..951a0b2815 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts"> -import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; +import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; import { focusPrev, focusNext } from '@/scripts/focus.js'; import MkSwitchButton from '@/components/MkSwitch.button.vue'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu'; @@ -90,19 +90,19 @@ const emit = defineEmits<{ (ev: 'hide'): void; }>(); -let itemsEl = $shallowRef<HTMLDivElement>(); +const itemsEl = shallowRef<HTMLDivElement>(); -let items2: InnerMenuItem[] = $ref([]); +const items2 = ref<InnerMenuItem[]>([]); -let child = $shallowRef<InstanceType<typeof XChild>>(); +const child = shallowRef<InstanceType<typeof XChild>>(); -let keymap = $computed(() => ({ +const keymap = computed(() => ({ 'up|k|shift+tab': focusUp, 'down|j|tab': focusDown, 'esc': close, })); -let childShowingItem = $ref<MenuItem | null>(); +const childShowingItem = ref<MenuItem | null>(); let preferClick = isTouchUsing || props.asDrawer; @@ -115,22 +115,22 @@ watch(() => props.items, () => { if (item && 'then' in item) { // if item is Promise items[i] = { type: 'pending' }; item.then(actualItem => { - items2[i] = actualItem; + items2.value[i] = actualItem; }); } } - items2 = items as InnerMenuItem[]; + items2.value = items as InnerMenuItem[]; }, { immediate: true, }); const childMenu = ref<MenuItem[] | null>(); -let childTarget = $shallowRef<HTMLElement | null>(); +const childTarget = shallowRef<HTMLElement | null>(); function closeChild() { childMenu.value = null; - childShowingItem = null; + childShowingItem.value = null; } function childActioned() { @@ -139,8 +139,8 @@ function childActioned() { } const onGlobalMousedown = (event: MouseEvent) => { - if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return; - if (child && child.checkHit(event)) return; + if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return; + if (child.value && child.value.checkHit(event)) return; closeChild(); }; @@ -177,10 +177,10 @@ async function showChildren(item: MenuParent, ev: MouseEvent) { }); emit('hide'); } else { - childTarget = ev.currentTarget ?? ev.target; + childTarget.value = ev.currentTarget ?? ev.target; // これでもリアクティビティは保たれる childMenu.value = children; - childShowingItem = item; + childShowingItem.value = item; } } @@ -209,7 +209,7 @@ function switchItem(item: MenuSwitch & { ref: any }) { onMounted(() => { if (props.viaKeyboard) { nextTick(() => { - if (itemsEl) focusNext(itemsEl.children[0], true, false); + if (itemsEl.value) focusNext(itemsEl.value.children[0], true, false); }); } diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue index 8d2a147306..f0a2c232bd 100644 --- a/packages/frontend/src/components/MkMiniChart.vue +++ b/packages/frontend/src/components/MkMiniChart.vue @@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch } from 'vue'; +import { watch, ref } from 'vue'; import { v4 as uuid } from 'uuid'; import tinycolor from 'tinycolor2'; import { useInterval } from '@/scripts/use-interval.js'; @@ -43,11 +43,11 @@ const props = defineProps<{ const viewBoxX = 50; const viewBoxY = 50; const gradientId = uuid(); -let polylinePoints = $ref(''); -let polygonPoints = $ref(''); -let headX = $ref<number | null>(null); -let headY = $ref<number | null>(null); -let clock = $ref<number | null>(null); +const polylinePoints = ref(''); +const polygonPoints = ref(''); +const headX = ref<number | null>(null); +const headY = ref<number | null>(null); +const clock = ref<number | null>(null); const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')); const color = accent.toRgbString(); @@ -60,12 +60,12 @@ function draw(): void { (1 - (n / peak)) * viewBoxY, ]); - polylinePoints = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' '); + polylinePoints.value = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' '); - polygonPoints = `0,${ viewBoxY } ${ polylinePoints } ${ viewBoxX },${ viewBoxY }`; + polygonPoints.value = `0,${ viewBoxY } ${ polylinePoints.value } ${ viewBoxX },${ viewBoxY }`; - headX = _polylinePoints.at(-1)![0]; - headY = _polylinePoints.at(-1)![1]; + headX.value = _polylinePoints.at(-1)![0]; + headY.value = _polylinePoints.at(-1)![1]; } watch(() => props.src, draw, { immediate: true }); diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue index ec5039c504..5cd31cdf7c 100644 --- a/packages/frontend/src/components/MkModal.vue +++ b/packages/frontend/src/components/MkModal.vue @@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch } from 'vue'; +import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch, ref, shallowRef, computed } from 'vue'; import * as os from '@/os.js'; import { isTouchUsing } from '@/scripts/touch.js'; import { defaultStore } from '@/store.js'; @@ -89,14 +89,14 @@ const emit = defineEmits<{ provide('modal', true); -let maxHeight = $ref<number>(); -let fixed = $ref(false); -let transformOrigin = $ref('center'); -let showing = $ref(true); -let content = $shallowRef<HTMLElement>(); +const maxHeight = ref<number>(); +const fixed = ref(false); +const transformOrigin = ref('center'); +const showing = ref(true); +const content = shallowRef<HTMLElement>(); const zIndex = os.claimZIndex(props.zPriority); -let useSendAnime = $ref(false); -const type = $computed<ModalTypes>(() => { +const useSendAnime = ref(false); +const type = computed<ModalTypes>(() => { if (props.preferType === 'auto') { if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') { return 'drawer'; @@ -107,26 +107,26 @@ const type = $computed<ModalTypes>(() => { return props.preferType!; } }); -const isEnableBgTransparent = $computed(() => props.transparentBg && (type === 'popup')); -let transitionName = $computed((() => +const isEnableBgTransparent = computed(() => props.transparentBg && (type.value === 'popup')); +const transitionName = computed((() => defaultStore.state.animation - ? useSendAnime + ? useSendAnime.value ? 'send' - : type === 'drawer' + : type.value === 'drawer' ? 'modal-drawer' - : type === 'popup' + : type.value === 'popup' ? 'modal-popup' : 'modal' : '' )); -let transitionDuration = $computed((() => - transitionName === 'send' +const transitionDuration = computed((() => + transitionName.value === 'send' ? 400 - : transitionName === 'modal-popup' + : transitionName.value === 'modal-popup' ? 100 - : transitionName === 'modal' + : transitionName.value === 'modal' ? 200 - : transitionName === 'modal-drawer' + : transitionName.value === 'modal-drawer' ? 200 : 0 )); @@ -135,12 +135,12 @@ let contentClicking = false; function close(opts: { useSendAnimation?: boolean } = {}) { if (opts.useSendAnimation) { - useSendAnime = true; + useSendAnime.value = true; } // eslint-disable-next-line vue/no-mutating-props if (props.src) props.src.style.pointerEvents = 'auto'; - showing = false; + showing.value = false; emit('close'); } @@ -149,8 +149,8 @@ function onBgClick() { emit('click'); } -if (type === 'drawer') { - maxHeight = window.innerHeight / 1.5; +if (type.value === 'drawer') { + maxHeight.value = window.innerHeight / 1.5; } const keymap = { @@ -162,21 +162,21 @@ const SCROLLBAR_THICKNESS = 16; const align = () => { if (props.src == null) return; - if (type === 'drawer') return; - if (type === 'dialog') return; + if (type.value === 'drawer') return; + if (type.value === 'dialog') return; - if (content == null) return; + if (content.value == null) return; const srcRect = props.src.getBoundingClientRect(); - const width = content!.offsetWidth; - const height = content!.offsetHeight; + const width = content.value!.offsetWidth; + const height = content.value!.offsetHeight; let left; let top; - const x = srcRect.left + (fixed ? 0 : window.pageXOffset); - const y = srcRect.top + (fixed ? 0 : window.pageYOffset); + const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset); + const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset); if (props.anchor.x === 'center') { left = x + (props.src.offsetWidth / 2) - (width / 2); @@ -194,7 +194,7 @@ const align = () => { top = y + props.src.offsetHeight; } - if (fixed) { + if (fixed.value) { // 画面から横にはみ出る場合 if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) { left = (window.innerWidth - SCROLLBAR_THICKNESS) - width; @@ -207,16 +207,16 @@ const align = () => { if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { if (props.noOverlap && props.anchor.x === 'center') { if (underSpace >= (upperSpace / 3)) { - maxHeight = underSpace; + maxHeight.value = underSpace; } else { - maxHeight = upperSpace; + maxHeight.value = upperSpace; top = (upperSpace + MARGIN) - height; } } else { top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height; } } else { - maxHeight = underSpace; + maxHeight.value = underSpace; } } else { // 画面から横にはみ出る場合 @@ -231,16 +231,16 @@ const align = () => { if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { if (props.noOverlap && props.anchor.x === 'center') { if (underSpace >= (upperSpace / 3)) { - maxHeight = underSpace; + maxHeight.value = underSpace; } else { - maxHeight = upperSpace; + maxHeight.value = upperSpace; top = window.pageYOffset + ((upperSpace + MARGIN) - height); } } else { top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1; } } else { - maxHeight = underSpace; + maxHeight.value = underSpace; } } @@ -255,29 +255,29 @@ const align = () => { let transformOriginX = 'center'; let transformOriginY = 'center'; - if (top >= srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)) { + if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.pageYOffset)) { transformOriginY = 'top'; - } else if ((top + height) <= srcRect.top + (fixed ? 0 : window.pageYOffset)) { + } else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) { transformOriginY = 'bottom'; } - if (left >= srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)) { + if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) { transformOriginX = 'left'; - } else if ((left + width) <= srcRect.left + (fixed ? 0 : window.pageXOffset)) { + } else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) { transformOriginX = 'right'; } - transformOrigin = `${transformOriginX} ${transformOriginY}`; + transformOrigin.value = `${transformOriginX} ${transformOriginY}`; - content.style.left = left + 'px'; - content.style.top = top + 'px'; + content.value.style.left = left + 'px'; + content.value.style.top = top + 'px'; }; const onOpened = () => { emit('opened'); // モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する - const el = content!.children[0]; + const el = content.value!.children[0]; el.addEventListener('mousedown', ev => { contentClicking = true; window.addEventListener('mouseup', ev => { @@ -299,7 +299,7 @@ onMounted(() => { // eslint-disable-next-line vue/no-mutating-props props.src.style.pointerEvents = 'none'; } - fixed = (type === 'drawer') || (getFixedContainer(props.src) != null); + fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null); await nextTick(); @@ -307,7 +307,7 @@ onMounted(() => { }, { immediate: true }); nextTick(() => { - alignObserver.observe(content!); + alignObserver.observe(content.value!); }); }); diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue index 1fdf0beb26..7e185e8453 100644 --- a/packages/frontend/src/components/MkModalWindow.vue +++ b/packages/frontend/src/components/MkModalWindow.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onUnmounted } from 'vue'; +import { onMounted, onUnmounted, shallowRef, ref } from 'vue'; import MkModal from './MkModal.vue'; const props = withDefaults(defineProps<{ @@ -44,14 +44,14 @@ const emit = defineEmits<{ (event: 'ok'): void; }>(); -let modal = $shallowRef<InstanceType<typeof MkModal>>(); -let rootEl = $shallowRef<HTMLElement>(); -let headerEl = $shallowRef<HTMLElement>(); -let bodyWidth = $ref(0); -let bodyHeight = $ref(0); +const modal = shallowRef<InstanceType<typeof MkModal>>(); +const rootEl = shallowRef<HTMLElement>(); +const headerEl = shallowRef<HTMLElement>(); +const bodyWidth = ref(0); +const bodyHeight = ref(0); const close = () => { - modal.close(); + modal.value.close(); }; const onBgClick = () => { @@ -67,14 +67,14 @@ const onKeydown = (evt) => { }; const ro = new ResizeObserver((entries, observer) => { - bodyWidth = rootEl.offsetWidth; - bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight; + bodyWidth.value = rootEl.value.offsetWidth; + bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight; }); onMounted(() => { - bodyWidth = rootEl.offsetWidth; - bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight; - ro.observe(rootEl); + bodyWidth.value = rootEl.value.offsetWidth; + bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight; + ro.observe(rootEl.value); }); onUnmounted(() => { diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 596895efb9..36e3b253a2 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -205,12 +205,12 @@ const emit = defineEmits<{ const inChannel = inject('inChannel', null); const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); -let note = $ref(deepClone(props.note)); +const note = ref(deepClone(props.note)); // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { - let result: Misskey.entities.Note | null = deepClone(note); + let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { result = await interruptor.handler(result); @@ -222,15 +222,15 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note = result; + note.value = result; }); } const isRenote = ( - note.renote != null && - note.text == null && - note.fileIds.length === 0 && - note.poll == null + note.value.renote != null && + note.value.text == null && + note.value.fileIds.length === 0 && + note.value.poll == null ); const el = shallowRef<HTMLElement>(); @@ -239,21 +239,21 @@ const renoteButton = shallowRef<HTMLElement>(); const renoteTime = shallowRef<HTMLElement>(); const reactButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>(); -let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note); -const isMyRenote = $i && ($i.id === note.userId); +const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value); +const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(false); -const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null); -const urls = $computed(() => parsed ? extractUrlFromMfm(parsed) : null); -const isLong = shouldCollapsed(appearNote, urls ?? []); -const collapsed = ref(appearNote.cw == null && isLong); +const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null); +const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value) : null); +const isLong = shouldCollapsed(appearNote.value, urls.value ?? []); +const collapsed = ref(appearNote.value.cw == null && isLong); const isDeleted = ref(false); -const muted = ref(checkMute(appearNote, $i?.mutedWords)); -const hardMuted = ref(props.withHardMute && checkMute(appearNote, $i?.hardMutedWords)); +const muted = ref(checkMute(appearNote.value, $i?.mutedWords)); +const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords)); const translation = ref<any>(null); const translating = ref(false); -const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance); -const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id)); -let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null))); +const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); +const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id)); +const renoteCollapsed = ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || (appearNote.value.myReaction != null))); function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean { if (mutedWords == null) return false; @@ -277,20 +277,20 @@ const keymap = { provide('react', (reaction: string) => { os.api('notes/reactions/create', { - noteId: appearNote.id, + noteId: appearNote.value.id, reaction: reaction, }); }); if (props.mock) { watch(() => props.note, (to) => { - note = deepClone(to); + note.value = deepClone(to); }, { deep: true }); } else { useNoteCapture({ rootEl: el, - note: $$(appearNote), - pureNote: $$(note), + note: appearNote, + pureNote: note, isDeletedRef: isDeleted, }); } @@ -298,7 +298,7 @@ if (props.mock) { if (!props.mock) { useTooltip(renoteButton, async (showing) => { const renotes = await os.api('notes/renotes', { - noteId: appearNote.id, + noteId: appearNote.value.id, limit: 11, }); @@ -309,7 +309,7 @@ if (!props.mock) { os.popup(MkUsersTooltip, { showing, users, - count: appearNote.renoteCount, + count: appearNote.value.renoteCount, targetElement: renoteButton.value, }, {}, 'closed'); }); @@ -319,7 +319,7 @@ function renote(viaKeyboard = false) { pleaseLogin(); showMovedDialog(); - const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock }); + const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock }); os.popupMenu(menu, renoteButton.value, { viaKeyboard, }); @@ -331,8 +331,8 @@ function reply(viaKeyboard = false): void { return; } os.post({ - reply: appearNote, - channel: appearNote.channel, + reply: appearNote.value, + channel: appearNote.value.channel, animation: !viaKeyboard, }, () => { focus(); @@ -342,7 +342,7 @@ function reply(viaKeyboard = false): void { function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); - if (appearNote.reactionAcceptance === 'likeOnly') { + if (appearNote.value.reactionAcceptance === 'likeOnly') { sound.play('reaction'); if (props.mock) { @@ -350,7 +350,7 @@ function react(viaKeyboard = false): void { } os.api('notes/reactions/create', { - noteId: appearNote.id, + noteId: appearNote.value.id, reaction: '❤️', }); const el = reactButton.value as HTMLElement | null | undefined; @@ -371,10 +371,10 @@ function react(viaKeyboard = false): void { } os.api('notes/reactions/create', { - noteId: appearNote.id, + noteId: appearNote.value.id, reaction: reaction, }); - if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) { + if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) { claimAchievement('reactWithoutRead'); } }, () => { @@ -417,7 +417,7 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } @@ -427,7 +427,7 @@ function menu(viaKeyboard = false): void { return; } - const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); os.popupMenu(menu, menuButton.value, { viaKeyboard, }).then(focus).finally(cleanup); @@ -438,7 +438,7 @@ async function clip() { return; } - os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus); + os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus); } function showRenoteMenu(viaKeyboard = false): void { @@ -453,7 +453,7 @@ function showRenoteMenu(viaKeyboard = false): void { danger: true, action: () => { os.api('notes/delete', { - noteId: note.id, + noteId: note.value.id, }); isDeleted.value = true; }, @@ -463,7 +463,7 @@ function showRenoteMenu(viaKeyboard = false): void { if (isMyRenote) { pleaseLogin(); os.popupMenu([ - getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote), + getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), null, getUnrenote(), ], renoteTime.value, { @@ -471,9 +471,9 @@ function showRenoteMenu(viaKeyboard = false): void { }); } else { os.popupMenu([ - getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote), + getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), null, - getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote), + getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote), $i.isModerator || $i.isAdmin ? getUnrenote() : undefined, ], renoteTime.value, { viaKeyboard: viaKeyboard, @@ -499,7 +499,7 @@ function focusAfter() { function readPromo() { os.api('promo/read', { - noteId: appearNote.id, + noteId: appearNote.value.id, }); isDeleted.value = true; } diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 31e97b6aaa..a8ccf26c4c 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -235,12 +235,12 @@ const props = defineProps<{ const inChannel = inject('inChannel', null); -let note = $ref(deepClone(props.note)); +const note = ref(deepClone(props.note)); // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { - let result: Misskey.entities.Note | null = deepClone(note); + let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { result = await interruptor.handler(result); @@ -252,15 +252,15 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note = result; + note.value = result; }); } const isRenote = ( - note.renote != null && - note.text == null && - note.fileIds.length === 0 && - note.poll == null + note.value.renote != null && + note.value.text == null && + note.value.fileIds.length === 0 && + note.value.poll == null ); const el = shallowRef<HTMLElement>(); @@ -269,19 +269,19 @@ const renoteButton = shallowRef<HTMLElement>(); const renoteTime = shallowRef<HTMLElement>(); const reactButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>(); -let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note); -const isMyRenote = $i && ($i.id === note.userId); +const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value); +const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(false); const isDeleted = ref(false); -const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false); +const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false); const translation = ref(null); const translating = ref(false); -const parsed = appearNote.text ? mfm.parse(appearNote.text) : null; +const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null; const urls = parsed ? extractUrlFromMfm(parsed) : null; -const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance); +const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); const conversation = ref<Misskey.entities.Note[]>([]); const replies = ref<Misskey.entities.Note[]>([]); -const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id); +const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i.id); const keymap = { 'r': () => reply(true), @@ -294,41 +294,41 @@ const keymap = { provide('react', (reaction: string) => { os.api('notes/reactions/create', { - noteId: appearNote.id, + noteId: appearNote.value.id, reaction: reaction, }); }); -let tab = $ref('replies'); -let reactionTabType = $ref(null); +const tab = ref('replies'); +const reactionTabType = ref(null); -const renotesPagination = $computed(() => ({ +const renotesPagination = computed(() => ({ endpoint: 'notes/renotes', limit: 10, params: { - noteId: appearNote.id, + noteId: appearNote.value.id, }, })); -const reactionsPagination = $computed(() => ({ +const reactionsPagination = computed(() => ({ endpoint: 'notes/reactions', limit: 10, params: { - noteId: appearNote.id, - type: reactionTabType, + noteId: appearNote.value.id, + type: reactionTabType.value, }, })); useNoteCapture({ rootEl: el, - note: $$(appearNote), - pureNote: $$(note), + note: appearNote, + pureNote: note, isDeletedRef: isDeleted, }); useTooltip(renoteButton, async (showing) => { const renotes = await os.api('notes/renotes', { - noteId: appearNote.id, + noteId: appearNote.value.id, limit: 11, }); @@ -339,7 +339,7 @@ useTooltip(renoteButton, async (showing) => { os.popup(MkUsersTooltip, { showing, users, - count: appearNote.renoteCount, + count: appearNote.value.renoteCount, targetElement: renoteButton.value, }, {}, 'closed'); }); @@ -348,7 +348,7 @@ function renote(viaKeyboard = false) { pleaseLogin(); showMovedDialog(); - const { menu } = getRenoteMenu({ note: note, renoteButton }); + const { menu } = getRenoteMenu({ note: note.value, renoteButton }); os.popupMenu(menu, renoteButton.value, { viaKeyboard, }); @@ -358,8 +358,8 @@ function reply(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); os.post({ - reply: appearNote, - channel: appearNote.channel, + reply: appearNote.value, + channel: appearNote.value.channel, animation: !viaKeyboard, }, () => { focus(); @@ -369,11 +369,11 @@ function reply(viaKeyboard = false): void { function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); - if (appearNote.reactionAcceptance === 'likeOnly') { + if (appearNote.value.reactionAcceptance === 'likeOnly') { sound.play('reaction'); os.api('notes/reactions/create', { - noteId: appearNote.id, + noteId: appearNote.value.id, reaction: '❤️', }); const el = reactButton.value as HTMLElement | null | undefined; @@ -389,10 +389,10 @@ function react(viaKeyboard = false): void { sound.play('reaction'); os.api('notes/reactions/create', { - noteId: appearNote.id, + noteId: appearNote.value.id, reaction: reaction, }); - if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) { + if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) { claimAchievement('reactWithoutRead'); } }, () => { @@ -423,20 +423,20 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } function menu(viaKeyboard = false): void { - const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }); os.popupMenu(menu, menuButton.value, { viaKeyboard, }).then(focus).finally(cleanup); } async function clip() { - os.popupMenu(await getNoteClipMenu({ note: note, isDeleted }), clipButton.value).then(focus); + os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted }), clipButton.value).then(focus); } function showRenoteMenu(viaKeyboard = false): void { @@ -448,7 +448,7 @@ function showRenoteMenu(viaKeyboard = false): void { danger: true, action: () => { os.api('notes/delete', { - noteId: note.id, + noteId: note.value.id, }); isDeleted.value = true; }, @@ -470,7 +470,7 @@ const repliesLoaded = ref(false); function loadReplies() { repliesLoaded.value = true; os.api('notes/children', { - noteId: appearNote.id, + noteId: appearNote.value.id, limit: 30, }).then(res => { replies.value = res; @@ -482,7 +482,7 @@ const conversationLoaded = ref(false); function loadConversation() { conversationLoaded.value = true; os.api('notes/conversation', { - noteId: appearNote.replyId, + noteId: appearNote.value.replyId, }).then(res => { conversation.value = res.reverse(); }); diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index f3ab6b2723..868f64a4b8 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkNoteHeader from '@/components/MkNoteHeader.vue'; import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; @@ -33,7 +33,7 @@ const props = defineProps<{ note: Misskey.entities.Note; }>(); -const showContent = $ref(false); +const showContent = ref(false); </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index 1e901a1fd6..5649ce1e6c 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -65,15 +65,15 @@ const props = withDefaults(defineProps<{ const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false); -let showContent = $ref(false); -let replies: Misskey.entities.Note[] = $ref([]); +const showContent = ref(false); +const replies = ref<Misskey.entities.Note[]>([]); if (props.detail) { os.api('notes/children', { noteId: props.note.id, limit: 5, }).then(res => { - replies = res; + replies.value = res; }); } </script> diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue index 3d5a56975b..6725776f43 100644 --- a/packages/frontend/src/components/MkNotificationSelectWindow.vue +++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, Ref } from 'vue'; +import { ref, Ref, shallowRef } from 'vue'; import MkSwitch from './MkSwitch.vue'; import MkInfo from './MkInfo.vue'; import MkButton from './MkButton.vue'; @@ -51,7 +51,7 @@ const props = withDefaults(defineProps<{ excludeTypes: () => [], }); -const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any); @@ -61,7 +61,7 @@ function ok() { .filter(type => !typesMap[type].value), }); - if (dialog) dialog.close(); + if (dialog.value) dialog.value.close(); } function disableAll() { diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 2a0082204a..cefef91285 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -43,7 +43,7 @@ const props = defineProps<{ const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); -let pagination = $computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? { +const pagination = computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? { endpoint: 'i/notifications-grouped' as const, limit: 20, params: computed(() => ({ diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue index 8c113bd777..1b0ec72e41 100644 --- a/packages/frontend/src/components/MkOmit.vue +++ b/packages/frontend/src/components/MkOmit.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onUnmounted } from 'vue'; +import { onMounted, onUnmounted, shallowRef, ref } from 'vue'; import { i18n } from '@/i18n.js'; const props = withDefaults(defineProps<{ @@ -22,13 +22,13 @@ const props = withDefaults(defineProps<{ maxHeight: 200, }); -let content = $shallowRef<HTMLElement>(); -let omitted = $ref(false); -let ignoreOmit = $ref(false); +const content = shallowRef<HTMLElement>(); +const omitted = ref(false); +const ignoreOmit = ref(false); const calcOmit = () => { - if (omitted || ignoreOmit) return; - omitted = content.offsetHeight > props.maxHeight; + if (omitted.value || ignoreOmit.value) return; + omitted.value = content.value.offsetHeight > props.maxHeight; }; const omitObserver = new ResizeObserver((entries, observer) => { @@ -37,7 +37,7 @@ const omitObserver = new ResizeObserver((entries, observer) => { onMounted(() => { calcOmit(); - omitObserver.observe(content); + omitObserver.observe(content.value); }); onUnmounted(() => { diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 1b1eb11444..441296e05d 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ComputedRef, onMounted, onUnmounted, provide, shallowRef } from 'vue'; +import { ComputedRef, onMounted, onUnmounted, provide, shallowRef, ref, computed } from 'vue'; import RouterView from '@/components/global/RouterView.vue'; import MkWindow from '@/components/MkWindow.vue'; import { popout as _popout } from '@/scripts/popout.js'; @@ -55,16 +55,16 @@ defineEmits<{ const router = new Router(routes, props.initialPath, !!$i, page(() => import('@/pages/not-found.vue'))); const contents = shallowRef<HTMLElement>(); -let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); -let windowEl = $shallowRef<InstanceType<typeof MkWindow>>(); -const history = $ref<{ path: string; key: any; }[]>([{ +const pageMetadata = ref<null | ComputedRef<PageMetadata>>(); +const windowEl = shallowRef<InstanceType<typeof MkWindow>>(); +const history = ref<{ path: string; key: any; }[]>([{ path: router.getCurrentPath(), key: router.getCurrentKey(), }]); -const buttonsLeft = $computed(() => { +const buttonsLeft = computed(() => { const buttons = []; - if (history.length > 1) { + if (history.value.length > 1) { buttons.push({ icon: 'ti ti-arrow-left', onClick: back, @@ -73,7 +73,7 @@ const buttonsLeft = $computed(() => { return buttons; }); -const buttonsRight = $computed(() => { +const buttonsRight = computed(() => { const buttons = [{ icon: 'ti ti-reload', title: i18n.ts.reload, @@ -86,21 +86,21 @@ const buttonsRight = $computed(() => { return buttons; }); -let reloadCount = $ref(0); +const reloadCount = ref(0); router.addListener('push', ctx => { - history.push({ path: ctx.path, key: ctx.key }); + history.value.push({ path: ctx.path, key: ctx.key }); }); provide('router', router); provideMetadataReceiver((info) => { - pageMetadata = info; + pageMetadata.value = info; }); provide('shouldOmitHeaderTitle', true); provide('shouldHeaderThin', true); provide('forceSpacerMin', true); -const contextmenu = $computed(() => ([{ +const contextmenu = computed(() => ([{ icon: 'ti ti-player-eject', text: i18n.ts.showInPage, action: expand, @@ -113,7 +113,7 @@ const contextmenu = $computed(() => ([{ text: i18n.ts.openInNewTab, action: () => { window.open(url + router.getCurrentPath(), '_blank'); - windowEl.close(); + windowEl.value.close(); }, }, { icon: 'ti ti-link', @@ -124,26 +124,26 @@ const contextmenu = $computed(() => ([{ }])); function back() { - history.pop(); - router.replace(history.at(-1)!.path, history.at(-1)!.key); + history.value.pop(); + router.replace(history.value.at(-1)!.path, history.value.at(-1)!.key); } function reload() { - reloadCount++; + reloadCount.value++; } function close() { - windowEl.close(); + windowEl.value.close(); } function expand() { mainRouter.push(router.getCurrentPath(), 'forcePage'); - windowEl.close(); + windowEl.value.close(); } function popout() { - _popout(router.getCurrentPath(), windowEl.$el); - windowEl.close(); + _popout(router.getCurrentPath(), windowEl.value.$el); + windowEl.value.close(); } useScrollPositionManager(() => getScrollContainer(contents.value), router); diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 57348cde53..07347eda29 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts"> -import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, watch } from 'vue'; +import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js'; @@ -105,12 +105,12 @@ const emit = defineEmits<{ (ev: 'status', error: boolean): void; }>(); -let rootEl = $shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); // 遡り中かどうか -let backed = $ref(false); +const backed = ref(false); -let scrollRemove = $ref<(() => void) | null>(null); +const scrollRemove = ref<(() => void) | null>(null); /** * 表示するアイテムのソース @@ -142,8 +142,8 @@ const { enableInfiniteScroll, } = defaultStore.reactiveState; -const contentEl = $computed(() => props.pagination.pageEl ?? rootEl); -const scrollableElement = $computed(() => contentEl ? getScrollContainer(contentEl) : document.body); +const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value); +const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : document.body); const visibility = useDocumentVisibility(); @@ -153,35 +153,35 @@ const BACKGROUND_PAUSE_WAIT_SEC = 10; // 先頭が表示されているかどうかを検出 // https://qiita.com/mkataigi/items/0154aefd2223ce23398e -let scrollObserver = $ref<IntersectionObserver>(); +const scrollObserver = ref<IntersectionObserver>(); -watch([() => props.pagination.reversed, $$(scrollableElement)], () => { - if (scrollObserver) scrollObserver.disconnect(); +watch([() => props.pagination.reversed, scrollableElement], () => { + if (scrollObserver.value) scrollObserver.value.disconnect(); - scrollObserver = new IntersectionObserver(entries => { - backed = entries[0].isIntersecting; + scrollObserver.value = new IntersectionObserver(entries => { + backed.value = entries[0].isIntersecting; }, { - root: scrollableElement, + root: scrollableElement.value, rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px', threshold: 0.01, }); }, { immediate: true }); -watch($$(rootEl), () => { - scrollObserver?.disconnect(); +watch(rootEl, () => { + scrollObserver.value?.disconnect(); nextTick(() => { - if (rootEl) scrollObserver?.observe(rootEl); + if (rootEl.value) scrollObserver.value?.observe(rootEl.value); }); }); -watch([$$(backed), $$(contentEl)], () => { - if (!backed) { - if (!contentEl) return; +watch([backed, contentEl], () => { + if (!backed.value) { + if (!contentEl.value) return; - scrollRemove = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl, executeQueue, TOLERANCE); + scrollRemove.value = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl.value, executeQueue, TOLERANCE); } else { - if (scrollRemove) scrollRemove(); - scrollRemove = null; + if (scrollRemove.value) scrollRemove.value(); + scrollRemove.value = null; } }); @@ -254,14 +254,14 @@ const fetchMore = async (): Promise<void> => { } const reverseConcat = _res => { - const oldHeight = scrollableElement ? scrollableElement.scrollHeight : getBodyScrollHeight(); - const oldScroll = scrollableElement ? scrollableElement.scrollTop : window.scrollY; + const oldHeight = scrollableElement.value ? scrollableElement.value.scrollHeight : getBodyScrollHeight(); + const oldScroll = scrollableElement.value ? scrollableElement.value.scrollTop : window.scrollY; items.value = concatMapWithArray(items.value, _res); return nextTick(() => { - if (scrollableElement) { - scroll(scrollableElement, { top: oldScroll + (scrollableElement.scrollHeight - oldHeight), behavior: 'instant' }); + if (scrollableElement.value) { + scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' }); } else { window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' }); } @@ -351,7 +351,7 @@ const appearFetchMoreAhead = async (): Promise<void> => { fetchMoreAppearTimeout(); }; -const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl!, TOLERANCE); +const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl.value!, TOLERANCE); watch(visibility, () => { if (visibility.value === 'hidden') { @@ -445,11 +445,11 @@ onActivated(() => { }); onDeactivated(() => { - isBackTop.value = props.pagination.reversed ? window.scrollY >= (rootEl ? rootEl.scrollHeight - window.innerHeight : 0) : window.scrollY === 0; + isBackTop.value = props.pagination.reversed ? window.scrollY >= (rootEl.value ? rootEl.value.scrollHeight - window.innerHeight : 0) : window.scrollY === 0; }); function toBottom() { - scrollToBottom(contentEl!); + scrollToBottom(contentEl.value!); } onBeforeMount(() => { @@ -477,13 +477,13 @@ onBeforeUnmount(() => { clearTimeout(preventAppearFetchMoreTimer.value); preventAppearFetchMoreTimer.value = null; } - scrollObserver?.disconnect(); + scrollObserver.value?.disconnect(); }); defineExpose({ items, queue, - backed, + backed: backed.value, more, reload, prepend, diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue index 3abca7826f..118f9a6a91 100644 --- a/packages/frontend/src/components/MkPasswordDialog.vue +++ b/packages/frontend/src/components/MkPasswordDialog.vue @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, shallowRef, ref } from 'vue'; import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; @@ -49,22 +49,22 @@ const emit = defineEmits<{ (ev: 'cancelled'): void; }>(); -const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); -const passwordInput = $shallowRef<InstanceType<typeof MkInput>>(); -const password = $ref(''); -const token = $ref(null); +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); +const passwordInput = shallowRef<InstanceType<typeof MkInput>>(); +const password = ref(''); +const token = ref(null); function onClose() { emit('cancelled'); - if (dialog) dialog.close(); + if (dialog.value) dialog.value.close(); } function done(res) { - emit('done', { password, token }); - if (dialog) dialog.close(); + emit('done', { password: password.value, token: token.value }); + if (dialog.value) dialog.value.close(); } onMounted(() => { - if (passwordInput) passwordInput.focus(); + if (passwordInput.value) passwordInput.value.focus(); }); </script> diff --git a/packages/frontend/src/components/MkPlusOneEffect.vue b/packages/frontend/src/components/MkPlusOneEffect.vue index 0bc98f4334..a741a3f7a8 100644 --- a/packages/frontend/src/components/MkPlusOneEffect.vue +++ b/packages/frontend/src/components/MkPlusOneEffect.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, ref } from 'vue'; import * as os from '@/os.js'; const props = withDefaults(defineProps<{ @@ -23,13 +23,13 @@ const emit = defineEmits<{ (ev: 'end'): void; }>(); -let up = $ref(false); +const up = ref(false); const zIndex = os.claimZIndex('middle'); const angle = (45 - (Math.random() * 90)) + 'deg'; onMounted(() => { window.setTimeout(() => { - up = true; + up.value = true; }, 10); window.setTimeout(() => { diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue index ee7dbecf05..67fac003bf 100644 --- a/packages/frontend/src/components/MkPopupMenu.vue +++ b/packages/frontend/src/components/MkPopupMenu.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { ref, shallowRef } from 'vue'; import MkModal from './MkModal.vue'; import MkMenu from './MkMenu.vue'; import { MenuItem } from '@/types/menu'; @@ -28,7 +28,7 @@ const emit = defineEmits<{ (ev: 'closing'): void; }>(); -let modal = $shallowRef<InstanceType<typeof MkModal>>(); +const modal = shallowRef<InstanceType<typeof MkModal>>(); const manualShowing = ref(true); const hiding = ref(false); @@ -60,14 +60,14 @@ function hide() { hiding.value = true; // closeは呼ぶ必要がある - modal?.close(); + modal.value?.close(); } function close() { manualShowing.value = false; // closeは呼ぶ必要がある - modal?.close(); + modal.value?.close(); } </script> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 13b615d104..9e5c4ca3f1 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, ref } from 'vue'; +import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import insertTextAtCursor from 'insert-text-at-cursor'; @@ -162,42 +162,42 @@ const emit = defineEmits<{ (ev: 'fileChangeSensitive', fileId: string, to: boolean): void; }>(); -const textareaEl = $shallowRef<HTMLTextAreaElement | null>(null); -const cwInputEl = $shallowRef<HTMLInputElement | null>(null); -const hashtagsInputEl = $shallowRef<HTMLInputElement | null>(null); -const visibilityButton = $shallowRef<HTMLElement | null>(null); +const textareaEl = shallowRef<HTMLTextAreaElement | null>(null); +const cwInputEl = shallowRef<HTMLInputElement | null>(null); +const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null); +const visibilityButton = shallowRef<HTMLElement | null>(null); -let posting = $ref(false); -let posted = $ref(false); -let text = $ref(props.initialText ?? ''); -let files = $ref(props.initialFiles ?? []); -let poll = $ref<{ +const posting = ref(false); +const posted = ref(false); +const text = ref(props.initialText ?? ''); +const files = ref(props.initialFiles ?? []); +const poll = ref<{ choices: string[]; multiple: boolean; expiresAt: string | null; expiredAfter: string | null; } | null>(null); -let useCw = $ref(false); -let showPreview = $ref(defaultStore.state.showPreview); -watch($$(showPreview), () => defaultStore.set('showPreview', showPreview)); -let cw = $ref<string | null>(null); -let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly); -let visibility = $ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]); -let visibleUsers = $ref([]); +const useCw = ref(false); +const showPreview = ref(defaultStore.state.showPreview); +watch(showPreview, () => defaultStore.set('showPreview', showPreview.value)); +const cw = ref<string | null>(null); +const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly); +const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]); +const visibleUsers = ref([]); if (props.initialVisibleUsers) { props.initialVisibleUsers.forEach(pushVisibleUser); } -let reactionAcceptance = $ref(defaultStore.state.reactionAcceptance); -let autocomplete = $ref(null); -let draghover = $ref(false); -let quoteId = $ref(null); -let hasNotSpecifiedMentions = $ref(false); -let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]')); -let imeText = $ref(''); -let showingOptions = $ref(false); +const reactionAcceptance = ref(defaultStore.state.reactionAcceptance); +const autocomplete = ref(null); +const draghover = ref(false); +const quoteId = ref(null); +const hasNotSpecifiedMentions = ref(false); +const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]')); +const imeText = ref(''); +const showingOptions = ref(false); const textAreaReadOnly = ref(false); -const draftKey = $computed((): string => { +const draftKey = computed((): string => { let key = props.channel ? `channel:${props.channel.id}` : ''; if (props.renote) { @@ -211,7 +211,7 @@ const draftKey = $computed((): string => { return key; }); -const placeholder = $computed((): string => { +const placeholder = computed((): string => { if (props.renote) { return i18n.ts._postForm.quotePlaceholder; } else if (props.reply) { @@ -231,7 +231,7 @@ const placeholder = $computed((): string => { } }); -const submitText = $computed((): string => { +const submitText = computed((): string => { return props.renote ? i18n.ts.quote : props.reply @@ -239,45 +239,45 @@ const submitText = $computed((): string => { : i18n.ts.note; }); -const textLength = $computed((): number => { - return (text + imeText).trim().length; +const textLength = computed((): number => { + return (text.value + imeText.value).trim().length; }); -const maxTextLength = $computed((): number => { +const maxTextLength = computed((): number => { return instance ? instance.maxNoteTextLength : 1000; }); -const canPost = $computed((): boolean => { - return !props.mock && !posting && !posted && - (1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && - (textLength <= maxTextLength) && - (!poll || poll.choices.length >= 2); +const canPost = computed((): boolean => { + return !props.mock && !posting.value && !posted.value && + (1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) && + (textLength.value <= maxTextLength.value) && + (!poll.value || poll.value.choices.length >= 2); }); -const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags')); -const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags')); +const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags')); +const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags')); -watch($$(text), () => { +watch(text, () => { checkMissingMention(); }, { immediate: true }); -watch($$(visibility), () => { +watch(visibility, () => { checkMissingMention(); }, { immediate: true }); -watch($$(visibleUsers), () => { +watch(visibleUsers, () => { checkMissingMention(); }, { deep: true, }); if (props.mention) { - text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; - text += ' '; + text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; + text.value += ' '; } if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) { - text = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `; + text.value = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `; } if (props.reply && props.reply.text != null) { @@ -295,32 +295,32 @@ if (props.reply && props.reply.text != null) { if ($i.username === x.username && (x.host == null || x.host === host)) continue; // 重複は除外 - if (text.includes(`${mention} `)) continue; + if (text.value.includes(`${mention} `)) continue; - text += `${mention} `; + text.value += `${mention} `; } } -if ($i?.isSilenced && visibility === 'public') { - visibility = 'home'; +if ($i?.isSilenced && visibility.value === 'public') { + visibility.value = 'home'; } if (props.channel) { - visibility = 'public'; - localOnly = true; // TODO: チャンネルが連合するようになった折には消す + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す } // 公開以外へのリプライ時は元の公開範囲を引き継ぐ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) { - if (props.reply.visibility === 'home' && visibility === 'followers') { - visibility = 'followers'; - } else if (['home', 'followers'].includes(props.reply.visibility) && visibility === 'specified') { - visibility = 'specified'; + if (props.reply.visibility === 'home' && visibility.value === 'followers') { + visibility.value = 'followers'; + } else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') { + visibility.value = 'specified'; } else { - visibility = props.reply.visibility; + visibility.value = props.reply.visibility; } - if (visibility === 'specified') { + if (visibility.value === 'specified') { if (props.reply.visibleUserIds) { os.api('users/show', { userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId), @@ -338,57 +338,57 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib } if (props.specified) { - visibility = 'specified'; + visibility.value = 'specified'; pushVisibleUser(props.specified); } // keep cw when reply if (defaultStore.state.keepCw && props.reply && props.reply.cw) { - useCw = true; - cw = props.reply.cw; + useCw.value = true; + cw.value = props.reply.cw; } function watchForDraft() { - watch($$(text), () => saveDraft()); - watch($$(useCw), () => saveDraft()); - watch($$(cw), () => saveDraft()); - watch($$(poll), () => saveDraft()); - watch($$(files), () => saveDraft(), { deep: true }); - watch($$(visibility), () => saveDraft()); - watch($$(localOnly), () => saveDraft()); + watch(text, () => saveDraft()); + watch(useCw, () => saveDraft()); + watch(cw, () => saveDraft()); + watch(poll, () => saveDraft()); + watch(files, () => saveDraft(), { deep: true }); + watch(visibility, () => saveDraft()); + watch(localOnly, () => saveDraft()); } function checkMissingMention() { - if (visibility === 'specified') { - const ast = mfm.parse(text); + if (visibility.value === 'specified') { + const ast = mfm.parse(text.value); for (const x of extractMentions(ast)) { - if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) { - hasNotSpecifiedMentions = true; + if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { + hasNotSpecifiedMentions.value = true; return; } } } - hasNotSpecifiedMentions = false; + hasNotSpecifiedMentions.value = false; } function addMissingMention() { - const ast = mfm.parse(text); + const ast = mfm.parse(text.value); for (const x of extractMentions(ast)) { - if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) { + if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { os.api('users/show', { username: x.username, host: x.host }).then(user => { - visibleUsers.push(user); + visibleUsers.value.push(user); }); } } } function togglePoll() { - if (poll) { - poll = null; + if (poll.value) { + poll.value = null; } else { - poll = { + poll.value = { choices: ['', ''], multiple: false, expiresAt: null, @@ -398,13 +398,13 @@ function togglePoll() { } function addTag(tag: string) { - insertTextAtCursor(textareaEl, ` #${tag} `); + insertTextAtCursor(textareaEl.value, ` #${tag} `); } function focus() { - if (textareaEl) { - textareaEl.focus(); - textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length); + if (textareaEl.value) { + textareaEl.value.focus(); + textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length); } } @@ -413,55 +413,55 @@ function chooseFileFrom(ev) { selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { for (const file of files_) { - files.push(file); + files.value.push(file); } }); } function detachFile(id) { - files = files.filter(x => x.id !== id); + files.value = files.value.filter(x => x.id !== id); } function updateFileSensitive(file, sensitive) { if (props.mock) { emit('fileChangeSensitive', file.id, sensitive); } - files[files.findIndex(x => x.id === file.id)].isSensitive = sensitive; + files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive; } function updateFileName(file, name) { - files[files.findIndex(x => x.id === file.id)].name = name; + files.value[files.value.findIndex(x => x.id === file.id)].name = name; } function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void { - files[files.findIndex(x => x.id === file.id)] = newFile; + files.value[files.value.findIndex(x => x.id === file.id)] = newFile; } function upload(file: File, name?: string): void { if (props.mock) return; uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { - files.push(res); + files.value.push(res); }); } function setVisibility() { if (props.channel) { - visibility = 'public'; - localOnly = true; // TODO: チャンネルが連合するようになった折には消す + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す return; } os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { - currentVisibility: visibility, + currentVisibility: visibility.value, isSilenced: $i?.isSilenced, - localOnly: localOnly, - src: visibilityButton, + localOnly: localOnly.value, + src: visibilityButton.value, }, { changeVisibility: v => { - visibility = v; + visibility.value = v; if (defaultStore.state.rememberNoteVisibility) { - defaultStore.set('visibility', visibility); + defaultStore.set('visibility', visibility.value); } }, }, 'closed'); @@ -469,14 +469,14 @@ function setVisibility() { async function toggleLocalOnly() { if (props.channel) { - visibility = 'public'; - localOnly = true; // TODO: チャンネルが連合するようになった折には消す + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す return; } const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo'); - if (!localOnly && neverShowInfo !== 'true') { + if (!localOnly.value && neverShowInfo !== 'true') { const confirm = await os.actions({ type: 'question', title: i18n.ts.disableFederationConfirm, @@ -506,7 +506,7 @@ async function toggleLocalOnly() { } } - localOnly = !localOnly; + localOnly.value = !localOnly.value; } async function toggleReactionAcceptance() { @@ -519,15 +519,15 @@ async function toggleReactionAcceptance() { { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, { value: 'likeOnly' as const, text: i18n.ts.likeOnly }, ], - default: reactionAcceptance, + default: reactionAcceptance.value, }); if (select.canceled) return; - reactionAcceptance = select.result; + reactionAcceptance.value = select.result; } function pushVisibleUser(user) { - if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) { - visibleUsers.push(user); + if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) { + visibleUsers.value.push(user); } } @@ -535,34 +535,34 @@ function addVisibleUser() { os.selectUser().then(user => { pushVisibleUser(user); - if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { - text = `@${Misskey.acct.toString(user)} ${text}`; + if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { + text.value = `@${Misskey.acct.toString(user)} ${text.value}`; } }); } function removeVisibleUser(user) { - visibleUsers = erase(user, visibleUsers); + visibleUsers.value = erase(user, visibleUsers.value); } function clear() { - text = ''; - files = []; - poll = null; - quoteId = null; + text.value = ''; + files.value = []; + poll.value = null; + quoteId.value = null; } function onKeydown(ev: KeyboardEvent) { - if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost) post(); + if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post(); if (ev.key === 'Escape') emit('esc'); } function onCompositionUpdate(ev: CompositionEvent) { - imeText = ev.data; + imeText.value = ev.data; } function onCompositionEnd(ev: CompositionEvent) { - imeText = ''; + imeText.value = ''; } async function onPaste(ev: ClipboardEvent) { @@ -580,7 +580,7 @@ async function onPaste(ev: ClipboardEvent) { const paste = ev.clipboardData.getData('text'); - if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) { + if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) { ev.preventDefault(); os.confirm({ @@ -588,11 +588,11 @@ async function onPaste(ev: ClipboardEvent) { text: i18n.ts.quoteQuestion, }).then(({ canceled }) => { if (canceled) { - insertTextAtCursor(textareaEl, paste); + insertTextAtCursor(textareaEl.value, paste); return; } - quoteId = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1]; + quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1]; }); } } @@ -603,7 +603,7 @@ function onDragover(ev) { const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; if (isFile || isDriveFile) { ev.preventDefault(); - draghover = true; + draghover.value = true; switch (ev.dataTransfer.effectAllowed) { case 'all': case 'uninitialized': @@ -624,15 +624,15 @@ function onDragover(ev) { } function onDragenter(ev) { - draghover = true; + draghover.value = true; } function onDragleave(ev) { - draghover = false; + draghover.value = false; } function onDrop(ev): void { - draghover = false; + draghover.value = false; // ファイルだったら if (ev.dataTransfer.files.length > 0) { @@ -645,7 +645,7 @@ function onDrop(ev): void { const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); - files.push(file); + files.value.push(file); ev.preventDefault(); } //#endregion @@ -656,16 +656,16 @@ function saveDraft() { const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); - draftData[draftKey] = { + draftData[draftKey.value] = { updatedAt: new Date(), data: { - text: text, - useCw: useCw, - cw: cw, - visibility: visibility, - localOnly: localOnly, - files: files, - poll: poll, + text: text.value, + useCw: useCw.value, + cw: cw.value, + visibility: visibility.value, + localOnly: localOnly.value, + files: files.value, + poll: poll.value, }, }; @@ -675,13 +675,13 @@ function saveDraft() { function deleteDraft() { const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); - delete draftData[draftKey]; + delete draftData[draftKey.value]; miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } async function post(ev?: MouseEvent) { - if (useCw && (cw == null || cw.trim() === '')) { + if (useCw.value && (cw.value == null || cw.value.trim() === '')) { os.alert({ type: 'error', text: i18n.ts.cwNotationRequired, @@ -700,13 +700,13 @@ async function post(ev?: MouseEvent) { if (props.mock) return; const annoying = - text.includes('$[x2') || - text.includes('$[x3') || - text.includes('$[x4') || - text.includes('$[scale') || - text.includes('$[position'); + text.value.includes('$[x2') || + text.value.includes('$[x3') || + text.value.includes('$[x4') || + text.value.includes('$[scale') || + text.value.includes('$[position'); - if (annoying && visibility === 'public') { + if (annoying && visibility.value === 'public') { const { canceled, result } = await os.actions({ type: 'warning', text: i18n.ts.thisPostMayBeAnnoying, @@ -726,26 +726,26 @@ async function post(ev?: MouseEvent) { if (canceled) return; if (result === 'cancel') return; if (result === 'home') { - visibility = 'home'; + visibility.value = 'home'; } } let postData = { - text: text === '' ? null : text, - fileIds: files.length > 0 ? files.map(f => f.id) : undefined, + text: text.value === '' ? null : text.value, + fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined, replyId: props.reply ? props.reply.id : undefined, - renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined, + renoteId: props.renote ? props.renote.id : quoteId.value ? quoteId.value : undefined, channelId: props.channel ? props.channel.id : undefined, - poll: poll, - cw: useCw ? cw ?? '' : null, - localOnly: localOnly, - visibility: visibility, - visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined, - reactionAcceptance, + poll: poll.value, + cw: useCw.value ? cw.value ?? '' : null, + localOnly: localOnly.value, + visibility: visibility.value, + visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined, + reactionAcceptance: reactionAcceptance.value, }; - if (withHashtags && hashtags && hashtags.trim() !== '') { - const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); + if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') { + const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; } @@ -762,15 +762,15 @@ async function post(ev?: MouseEvent) { let token = undefined; - if (postAccount) { + if (postAccount.value) { const storedAccounts = await getAccounts(); - token = storedAccounts.find(x => x.id === postAccount.id)?.token; + token = storedAccounts.find(x => x.id === postAccount.value.id)?.token; } - posting = true; + posting.value = true; os.api('notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { - posted = true; + posted.value = true; } else { clear(); } @@ -782,8 +782,8 @@ async function post(ev?: MouseEvent) { const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); } - posting = false; - postAccount = null; + posting.value = false; + postAccount.value = null; incNotesCount(); if (notesCount === 1) { @@ -828,7 +828,7 @@ async function post(ev?: MouseEvent) { } }); }).catch(err => { - posting = false; + posting.value = false; os.alert({ type: 'error', text: err.message + '\n' + (err as any).id, @@ -842,7 +842,7 @@ function cancel() { function insertMention() { os.selectUser().then(user => { - insertTextAtCursor(textareaEl, '@' + Misskey.acct.toString(user) + ' '); + insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' '); }); } @@ -852,7 +852,7 @@ async function insertEmoji(ev: MouseEvent) { emojiPicker.show( ev.currentTarget ?? ev.target, emoji => { - insertTextAtCursor(textareaEl, emoji); + insertTextAtCursor(textareaEl.value, emoji); }, () => { textAreaReadOnly.value = false; @@ -866,17 +866,17 @@ function showActions(ev) { text: action.title, action: () => { action.handler({ - text: text, - cw: cw, + text: text.value, + cw: cw.value, }, (key, value) => { - if (key === 'text') { text = value; } - if (key === 'cw') { useCw = value !== null; cw = value; } + if (key === 'text') { text.value = value; } + if (key === 'cw') { useCw.value = value !== null; cw.value = value; } }); }, })), ev.currentTarget ?? ev.target); } -let postAccount = $ref<Misskey.entities.UserDetailed | null>(null); +const postAccount = ref<Misskey.entities.UserDetailed | null>(null); function openAccountMenu(ev: MouseEvent) { if (props.mock) return; @@ -884,12 +884,12 @@ function openAccountMenu(ev: MouseEvent) { openAccountMenu_({ withExtraOperation: false, includeCurrentAccount: true, - active: postAccount != null ? postAccount.id : $i.id, + active: postAccount.value != null ? postAccount.value.id : $i.id, onChoose: (account) => { if (account.id === $i.id) { - postAccount = null; + postAccount.value = null; } else { - postAccount = account; + postAccount.value = account; } }, }, ev); @@ -905,23 +905,23 @@ onMounted(() => { } // TODO: detach when unmount - new Autocomplete(textareaEl, $$(text)); - new Autocomplete(cwInputEl, $$(cw)); - new Autocomplete(hashtagsInputEl, $$(hashtags)); + new Autocomplete(textareaEl.value, text); + new Autocomplete(cwInputEl.value, cw); + new Autocomplete(hashtagsInputEl.value, hashtags); nextTick(() => { // 書きかけの投稿を復元 if (!props.instant && !props.mention && !props.specified && !props.mock) { - const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey]; + const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value]; if (draft) { - text = draft.data.text; - useCw = draft.data.useCw; - cw = draft.data.cw; - visibility = draft.data.visibility; - localOnly = draft.data.localOnly; - files = (draft.data.files || []).filter(draftFile => draftFile); + text.value = draft.data.text; + useCw.value = draft.data.useCw; + cw.value = draft.data.cw; + visibility.value = draft.data.visibility; + localOnly.value = draft.data.localOnly; + files.value = (draft.data.files || []).filter(draftFile => draftFile); if (draft.data.poll) { - poll = draft.data.poll; + poll.value = draft.data.poll; } } } @@ -929,21 +929,21 @@ onMounted(() => { // 削除して編集 if (props.initialNote) { const init = props.initialNote; - text = init.text ? init.text : ''; - files = init.files; - cw = init.cw; - useCw = init.cw != null; + text.value = init.text ? init.text : ''; + files.value = init.files; + cw.value = init.cw; + useCw.value = init.cw != null; if (init.poll) { - poll = { + poll.value = { choices: init.poll.choices.map(x => x.text), multiple: init.poll.multiple, expiresAt: init.poll.expiresAt, expiredAfter: init.poll.expiredAfter, }; } - visibility = init.visibility; - localOnly = init.localOnly; - quoteId = init.renote ? init.renote.id : null; + visibility.value = init.visibility; + localOnly.value = init.localOnly; + quoteId.value = init.renote ? init.renote.id : null; } nextTick(() => watchForDraft()); diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue index c07a166a83..a0fad1ab41 100644 --- a/packages/frontend/src/components/MkPostFormDialog.vue +++ b/packages/frontend/src/components/MkPostFormDialog.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import MkModal from '@/components/MkModal.vue'; import MkPostForm from '@/components/MkPostForm.vue'; @@ -36,11 +36,11 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -let modal = $shallowRef<InstanceType<typeof MkModal>>(); -let form = $shallowRef<InstanceType<typeof MkPostForm>>(); +const modal = shallowRef<InstanceType<typeof MkModal>>(); +const form = shallowRef<InstanceType<typeof MkPostForm>>(); function onPosted() { - modal.close({ + modal.value.close({ useSendAnimation: true, }); } diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index 00c1d3808e..44555f2c13 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onUnmounted, watch } from 'vue'; +import { onMounted, onUnmounted, watch, ref, shallowRef } from 'vue'; import { deviceKind } from '@/scripts/device-kind.js'; import { i18n } from '@/i18n.js'; import { getScrollContainer } from '@/scripts/scroll.js'; @@ -35,15 +35,15 @@ const RELEASE_TRANSITION_DURATION = 200; const PULL_BRAKE_BASE = 1.5; const PULL_BRAKE_FACTOR = 170; -let isPullStart = $ref(false); -let isPullEnd = $ref(false); -let isRefreshing = $ref(false); -let pullDistance = $ref(0); +const isPullStart = ref(false); +const isPullEnd = ref(false); +const isRefreshing = ref(false); +const pullDistance = ref(0); let supportPointerDesktop = false; let startScreenY: number | null = null; -const rootEl = $shallowRef<HTMLDivElement>(); +const rootEl = shallowRef<HTMLDivElement>(); let scrollEl: HTMLElement | null = null; let disabled = false; @@ -66,17 +66,17 @@ function getScreenY(event) { } function moveStart(event) { - if (!isPullStart && !isRefreshing && !disabled) { - isPullStart = true; + if (!isPullStart.value && !isRefreshing.value && !disabled) { + isPullStart.value = true; startScreenY = getScreenY(event); - pullDistance = 0; + pullDistance.value = 0; } } function moveBySystem(to: number): Promise<void> { return new Promise(r => { - const startHeight = pullDistance; - const overHeight = pullDistance - to; + const startHeight = pullDistance.value; + const overHeight = pullDistance.value - to; if (overHeight < 1) { r(); return; @@ -85,36 +85,36 @@ function moveBySystem(to: number): Promise<void> { let intervalId = setInterval(() => { const time = Date.now() - startTime; if (time > RELEASE_TRANSITION_DURATION) { - pullDistance = to; + pullDistance.value = to; clearInterval(intervalId); r(); return; } const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time; - if (pullDistance < nextHeight) return; - pullDistance = nextHeight; + if (pullDistance.value < nextHeight) return; + pullDistance.value = nextHeight; }, 1); }); } async function fixOverContent() { - if (pullDistance > FIRE_THRESHOLD) { + if (pullDistance.value > FIRE_THRESHOLD) { await moveBySystem(FIRE_THRESHOLD); } } async function closeContent() { - if (pullDistance > 0) { + if (pullDistance.value > 0) { await moveBySystem(0); } } function moveEnd() { - if (isPullStart && !isRefreshing) { + if (isPullStart.value && !isRefreshing.value) { startScreenY = null; - if (isPullEnd) { - isPullEnd = false; - isRefreshing = true; + if (isPullEnd.value) { + isPullEnd.value = false; + isRefreshing.value = true; fixOverContent().then(() => { emit('refresh'); props.refresher().then(() => { @@ -122,17 +122,17 @@ function moveEnd() { }); }); } else { - closeContent().then(() => isPullStart = false); + closeContent().then(() => isPullStart.value = false); } } } function moving(event: TouchEvent | PointerEvent) { - if (!isPullStart || isRefreshing || disabled) return; + if (!isPullStart.value || isRefreshing.value || disabled) return; - if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance)) { - pullDistance = 0; - isPullEnd = false; + if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value)) { + pullDistance.value = 0; + isPullEnd.value = false; moveEnd(); return; } @@ -143,13 +143,13 @@ function moving(event: TouchEvent | PointerEvent) { const moveScreenY = getScreenY(event); const moveHeight = moveScreenY - startScreenY!; - pullDistance = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE); + pullDistance.value = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE); - if (pullDistance > 0) { + if (pullDistance.value > 0) { if (event.cancelable) event.preventDefault(); } - isPullEnd = pullDistance >= FIRE_THRESHOLD; + isPullEnd.value = pullDistance.value >= FIRE_THRESHOLD; } /** @@ -159,8 +159,8 @@ function moving(event: TouchEvent | PointerEvent) { */ function refreshFinished() { closeContent().then(() => { - isPullStart = false; - isRefreshing = false; + isPullStart.value = false; + isRefreshing.value = false; }); } @@ -182,26 +182,26 @@ function onScrollContainerScroll() { } function registerEventListenersForReadyToPull() { - if (rootEl == null) return; - rootEl.addEventListener('touchstart', moveStart, { passive: true }); - rootEl.addEventListener('touchmove', moving, { passive: false }); // passive: falseにしないとpreventDefaultが使えない + if (rootEl.value == null) return; + rootEl.value.addEventListener('touchstart', moveStart, { passive: true }); + rootEl.value.addEventListener('touchmove', moving, { passive: false }); // passive: falseにしないとpreventDefaultが使えない } function unregisterEventListenersForReadyToPull() { - if (rootEl == null) return; - rootEl.removeEventListener('touchstart', moveStart); - rootEl.removeEventListener('touchmove', moving); + if (rootEl.value == null) return; + rootEl.value.removeEventListener('touchstart', moveStart); + rootEl.value.removeEventListener('touchmove', moving); } onMounted(() => { - if (rootEl == null) return; + if (rootEl.value == null) return; - scrollEl = getScrollContainer(rootEl); + scrollEl = getScrollContainer(rootEl.value); if (scrollEl == null) return; scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true }); - rootEl.addEventListener('touchend', moveEnd, { passive: true }); + rootEl.value.addEventListener('touchend', moveEnd, { passive: true }); registerEventListenersForReadyToPull(); }); diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue index ba64775298..ebbd5e6cdc 100644 --- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue +++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue @@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script setup lang="ts"> +import { ref } from 'vue'; import { $i, getAccounts } from '@/account.js'; import MkButton from '@/components/MkButton.vue'; import { instance } from '@/instance.js'; @@ -62,26 +63,26 @@ defineProps<{ }>(); // ServiceWorker registration -let registration = $ref<ServiceWorkerRegistration | undefined>(); +const registration = ref<ServiceWorkerRegistration | undefined>(); // If this browser supports push notification -let supported = $ref(false); +const supported = ref(false); // If this browser has already subscribed to push notification -let pushSubscription = $ref<PushSubscription | null>(null); -let pushRegistrationInServer = $ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>(); +const pushSubscription = ref<PushSubscription | null>(null); +const pushRegistrationInServer = ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>(); function subscribe() { - if (!registration || !supported || !instance.swPublickey) return; + if (!registration.value || !supported.value || !instance.swPublickey) return; // SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters - return promiseDialog(registration.pushManager.subscribe({ + return promiseDialog(registration.value.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(instance.swPublickey), }) .then(async subscription => { - pushSubscription = subscription; + pushSubscription.value = subscription; // Register - pushRegistrationInServer = await api('sw/register', { + pushRegistrationInServer.value = await api('sw/register', { endpoint: subscription.endpoint, auth: encode(subscription.getKey('auth')), publickey: encode(subscription.getKey('p256dh')), @@ -102,12 +103,12 @@ function subscribe() { } async function unsubscribe() { - if (!pushSubscription) return; + if (!pushSubscription.value) return; - const endpoint = pushSubscription.endpoint; + const endpoint = pushSubscription.value.endpoint; const accounts = await getAccounts(); - pushRegistrationInServer = undefined; + pushRegistrationInServer.value = undefined; if ($i && accounts.length >= 2) { apiWithDialog('sw/unregister', { @@ -115,11 +116,11 @@ async function unsubscribe() { endpoint, }); } else { - pushSubscription.unsubscribe(); + pushSubscription.value.unsubscribe(); apiWithDialog('sw/unregister', { endpoint, }); - pushSubscription = null; + pushSubscription.value = null; } } @@ -150,20 +151,20 @@ if (navigator.serviceWorker == null) { // TODO: よしなに? } else { navigator.serviceWorker.ready.then(async swr => { - registration = swr; + registration.value = swr; - pushSubscription = await registration.pushManager.getSubscription(); + pushSubscription.value = await registration.value.pushManager.getSubscription(); if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) { - supported = true; + supported.value = true; - if (pushSubscription) { + if (pushSubscription.value) { const res = await api('sw/show-registration', { - endpoint: pushSubscription.endpoint, + endpoint: pushSubscription.value.endpoint, }); if (res) { - pushRegistrationInServer = res; + pushRegistrationInServer.value = res; } } } @@ -171,6 +172,6 @@ if (navigator.serviceWorker == null) { } defineExpose({ - pushRegistrationInServer: $$(pushRegistrationInServer), + pushRegistrationInServer: pushRegistrationInServer, }); </script> diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index c4df3e991b..2d68557aad 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { computed } from 'vue'; const props = defineProps<{ modelValue: any; @@ -36,7 +36,7 @@ const emit = defineEmits<{ (ev: 'update:modelValue', value: any): void; }>(); -let checked = $computed(() => props.modelValue === props.value); +const checked = computed(() => props.modelValue === props.value); function toggle(): void { if (props.disabled) return; diff --git a/packages/frontend/src/components/MkReactionEffect.vue b/packages/frontend/src/components/MkReactionEffect.vue index 88e262d880..75eb91e7ad 100644 --- a/packages/frontend/src/components/MkReactionEffect.vue +++ b/packages/frontend/src/components/MkReactionEffect.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, ref } from 'vue'; import * as os from '@/os.js'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; @@ -27,13 +27,13 @@ const emit = defineEmits<{ (ev: 'end'): void; }>(); -let up = $ref(false); +const up = ref(false); const zIndex = os.claimZIndex('middle'); const angle = (90 - (Math.random() * 180)) + 'deg'; onMounted(() => { window.setTimeout(() => { - up = true; + up.value = true; }, 10); window.setTimeout(() => { diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue index eaa7faa4f9..a14f2512f8 100644 --- a/packages/frontend/src/components/MkReactionsViewer.vue +++ b/packages/frontend/src/components/MkReactionsViewer.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import * as Misskey from 'misskey-js'; -import { inject, watch } from 'vue'; +import { inject, watch, ref } from 'vue'; import XReaction from '@/components/MkReactionsViewer.reaction.vue'; import { defaultStore } from '@/store.js'; @@ -38,31 +38,31 @@ const emit = defineEmits<{ const initialReactions = new Set(Object.keys(props.note.reactions)); -let reactions = $ref<[string, number][]>([]); -let hasMoreReactions = $ref(false); +const reactions = ref<[string, number][]>([]); +const hasMoreReactions = ref(false); -if (props.note.myReaction && !Object.keys(reactions).includes(props.note.myReaction)) { - reactions[props.note.myReaction] = props.note.reactions[props.note.myReaction]; +if (props.note.myReaction && !Object.keys(reactions.value).includes(props.note.myReaction)) { + reactions.value[props.note.myReaction] = props.note.reactions[props.note.myReaction]; } function onMockToggleReaction(emoji: string, count: number) { if (!mock) return; - const i = reactions.findIndex((item) => item[0] === emoji); + const i = reactions.value.findIndex((item) => item[0] === emoji); if (i < 0) return; - emit('mockUpdateMyReaction', emoji, (count - reactions[i][1])); + emit('mockUpdateMyReaction', emoji, (count - reactions.value[i][1])); } watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => { let newReactions: [string, number][] = []; - hasMoreReactions = Object.keys(newSource).length > maxNumber; + hasMoreReactions.value = Object.keys(newSource).length > maxNumber; - for (let i = 0; i < reactions.length; i++) { - const reaction = reactions[i][0]; + for (let i = 0; i < reactions.value.length; i++) { + const reaction = reactions.value[i][0]; if (reaction in newSource && newSource[reaction] !== 0) { - reactions[i][1] = newSource[reaction]; - newReactions.push(reactions[i]); + reactions.value[i][1] = newSource[reaction]; + newReactions.push(reactions.value[i]); } } @@ -80,7 +80,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]); } - reactions = newReactions; + reactions.value = newReactions; }, { immediate: true, deep: true }); </script> diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue index 3dc9a94ae2..e69aa1be80 100644 --- a/packages/frontend/src/components/MkRetentionHeatmap.vue +++ b/packages/frontend/src/components/MkRetentionHeatmap.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, nextTick } from 'vue'; +import { onMounted, nextTick, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; @@ -23,11 +23,11 @@ import { initChart } from '@/scripts/init-chart.js'; initChart(); -const rootEl = $shallowRef<HTMLDivElement>(null); -const chartEl = $shallowRef<HTMLCanvasElement>(null); +const rootEl = shallowRef<HTMLDivElement>(null); +const chartEl = shallowRef<HTMLCanvasElement>(null); const now = new Date(); let chartInstance: Chart = null; -let fetching = $ref(true); +const fetching = ref(true); const { handler: externalTooltipHandler } = useChartTooltip({ position: 'middle', @@ -38,8 +38,8 @@ async function renderChart() { chartInstance.destroy(); } - const wide = rootEl.offsetWidth > 600; - const narrow = rootEl.offsetWidth < 400; + const wide = rootEl.value.offsetWidth > 600; + const narrow = rootEl.value.offsetWidth < 400; const maxDays = wide ? 10 : narrow ? 5 : 7; @@ -66,7 +66,7 @@ async function renderChart() { } } - fetching = false; + fetching.value = false; await nextTick(); @@ -83,7 +83,7 @@ async function renderChart() { const marginEachCell = 12; - chartInstance = new Chart(chartEl, { + chartInstance = new Chart(chartEl.value, { type: 'matrix', data: { datasets: [{ diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index 0fd67a0b46..6051db1cad 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent } from 'vue'; +import { defineAsyncComponent, ref } from 'vue'; import { toUnicode } from 'punycode/'; import * as Misskey from 'misskey-js'; import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill'; @@ -62,17 +62,17 @@ import * as os from '@/os.js'; import { login } from '@/account.js'; import { i18n } from '@/i18n.js'; -let signing = $ref(false); -let user = $ref<Misskey.entities.UserDetailed | null>(null); -let username = $ref(''); -let password = $ref(''); -let token = $ref(''); -let host = $ref(toUnicode(configHost)); -let totpLogin = $ref(false); -let queryingKey = $ref(false); -let credentialRequest = $ref<CredentialRequestOptions | null>(null); -let hCaptchaResponse = $ref(null); -let reCaptchaResponse = $ref(null); +const signing = ref(false); +const user = ref<Misskey.entities.UserDetailed | null>(null); +const username = ref(''); +const password = ref(''); +const token = ref(''); +const host = ref(toUnicode(configHost)); +const totpLogin = ref(false); +const queryingKey = ref(false); +const credentialRequest = ref<CredentialRequestOptions | null>(null); +const hCaptchaResponse = ref(null); +const reCaptchaResponse = ref(null); const emit = defineEmits<{ (ev: 'login', v: any): void; @@ -98,11 +98,11 @@ const props = defineProps({ function onUsernameChange(): void { os.api('users/show', { - username: username, + username: username.value, }).then(userResponse => { - user = userResponse; + user.value = userResponse; }, () => { - user = null; + user.value = null; }); } @@ -113,21 +113,21 @@ function onLogin(res: any): Promise<void> | void { } async function queryKey(): Promise<void> { - queryingKey = true; - await webAuthnRequest(credentialRequest) + queryingKey.value = true; + await webAuthnRequest(credentialRequest.value) .catch(() => { - queryingKey = false; + queryingKey.value = false; return Promise.reject(null); }).then(credential => { - credentialRequest = null; - queryingKey = false; - signing = true; + credentialRequest.value = null; + queryingKey.value = false; + signing.value = true; return os.api('signin', { - username, - password, + username: username.value, + password: password.value, credential: credential.toJSON(), - 'hcaptcha-response': hCaptchaResponse, - 'g-recaptcha-response': reCaptchaResponse, + 'hcaptcha-response': hCaptchaResponse.value, + 'g-recaptcha-response': reCaptchaResponse.value, }); }).then(res => { emit('login', res); @@ -138,39 +138,39 @@ async function queryKey(): Promise<void> { type: 'error', text: i18n.ts.signinFailed, }); - signing = false; + signing.value = false; }); } function onSubmit(): void { - signing = true; - if (!totpLogin && user && user.twoFactorEnabled) { - if (webAuthnSupported() && user.securityKeys) { + signing.value = true; + if (!totpLogin.value && user.value && user.value.twoFactorEnabled) { + if (webAuthnSupported() && user.value.securityKeys) { os.api('signin', { - username, - password, - 'hcaptcha-response': hCaptchaResponse, - 'g-recaptcha-response': reCaptchaResponse, + username: username.value, + password: password.value, + 'hcaptcha-response': hCaptchaResponse.value, + 'g-recaptcha-response': reCaptchaResponse.value, }).then(res => { - totpLogin = true; - signing = false; - credentialRequest = parseRequestOptionsFromJSON({ + totpLogin.value = true; + signing.value = false; + credentialRequest.value = parseRequestOptionsFromJSON({ publicKey: res, }); }) .then(() => queryKey()) .catch(loginFailed); } else { - totpLogin = true; - signing = false; + totpLogin.value = true; + signing.value = false; } } else { os.api('signin', { - username, - password, - 'hcaptcha-response': hCaptchaResponse, - 'g-recaptcha-response': reCaptchaResponse, - token: user?.twoFactorEnabled ? token : undefined, + username: username.value, + password: password.value, + 'hcaptcha-response': hCaptchaResponse.value, + 'g-recaptcha-response': reCaptchaResponse.value, + token: user.value?.twoFactorEnabled ? token.value : undefined, }).then(res => { emit('login', res); onLogin(res); @@ -218,8 +218,8 @@ function loginFailed(err: any): void { } } - totpLogin = false; - signing = false; + totpLogin.value = false; + signing.value = false; } function resetPassword(): void { diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue index 05cef6ed3b..6f961cff05 100644 --- a/packages/frontend/src/components/MkSigninDialog.vue +++ b/packages/frontend/src/components/MkSigninDialog.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { shallowRef } from 'vue'; import MkSignin from '@/components/MkSignin.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; @@ -39,15 +39,15 @@ const emit = defineEmits<{ (ev: 'cancelled'): void; }>(); -const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); function onClose() { emit('cancelled'); - if (dialog) dialog.close(); + if (dialog.value) dialog.value.close(); } function onLogin(res) { emit('done', res); - if (dialog) dialog.close(); + if (dialog.value) dialog.value.close(); } </script> diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index a67251eda1..08e57fd8a6 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref, computed } from 'vue'; import { toUnicode } from 'punycode/'; import MkButton from './MkButton.vue'; import MkInput from './MkInput.vue'; @@ -101,34 +101,34 @@ const emit = defineEmits<{ const host = toUnicode(config.host); -let hcaptcha = $ref<Captcha | undefined>(); -let recaptcha = $ref<Captcha | undefined>(); -let turnstile = $ref<Captcha | undefined>(); +const hcaptcha = ref<Captcha | undefined>(); +const recaptcha = ref<Captcha | undefined>(); +const turnstile = ref<Captcha | undefined>(); -let username: string = $ref(''); -let password: string = $ref(''); -let retypedPassword: string = $ref(''); -let invitationCode: string = $ref(''); -let email = $ref(''); -let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null); -let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null); -let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref(''); -let passwordRetypeState: null | 'match' | 'not-match' = $ref(null); -let submitting: boolean = $ref(false); -let hCaptchaResponse = $ref(null); -let reCaptchaResponse = $ref(null); -let turnstileResponse = $ref(null); -let usernameAbortController: null | AbortController = $ref(null); -let emailAbortController: null | AbortController = $ref(null); +const username = ref<string>(''); +const password = ref<string>(''); +const retypedPassword = ref<string>(''); +const invitationCode = ref<string>(''); +const email = ref(''); +const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null); +const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null); +const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>(''); +const passwordRetypeState = ref<null | 'match' | 'not-match'>(null); +const submitting = ref<boolean>(false); +const hCaptchaResponse = ref(null); +const reCaptchaResponse = ref(null); +const turnstileResponse = ref(null); +const usernameAbortController = ref<null | AbortController>(null); +const emailAbortController = ref<null | AbortController>(null); -const shouldDisableSubmitting = $computed((): boolean => { - return submitting || - instance.enableHcaptcha && !hCaptchaResponse || - instance.enableRecaptcha && !reCaptchaResponse || - instance.enableTurnstile && !turnstileResponse || - instance.emailRequiredForSignup && emailState !== 'ok' || - usernameState !== 'ok' || - passwordRetypeState !== 'match'; +const shouldDisableSubmitting = computed((): boolean => { + return submitting.value || + instance.enableHcaptcha && !hCaptchaResponse.value || + instance.enableRecaptcha && !reCaptchaResponse.value || + instance.enableTurnstile && !turnstileResponse.value || + instance.emailRequiredForSignup && emailState.value !== 'ok' || + usernameState.value !== 'ok' || + passwordRetypeState.value !== 'match'; }); function getPasswordStrength(source: string): number { @@ -156,57 +156,57 @@ function getPasswordStrength(source: string): number { } function onChangeUsername(): void { - if (username === '') { - usernameState = null; + if (username.value === '') { + usernameState.value = null; return; } { const err = - !username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' : - username.length < 1 ? 'min-range' : - username.length > 20 ? 'max-range' : + !username.value.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' : + username.value.length < 1 ? 'min-range' : + username.value.length > 20 ? 'max-range' : null; if (err) { - usernameState = err; + usernameState.value = err; return; } } - if (usernameAbortController != null) { - usernameAbortController.abort(); + if (usernameAbortController.value != null) { + usernameAbortController.value.abort(); } - usernameState = 'wait'; - usernameAbortController = new AbortController(); + usernameState.value = 'wait'; + usernameAbortController.value = new AbortController(); os.api('username/available', { - username, - }, undefined, usernameAbortController.signal).then(result => { - usernameState = result.available ? 'ok' : 'unavailable'; + username: username.value, + }, undefined, usernameAbortController.value.signal).then(result => { + usernameState.value = result.available ? 'ok' : 'unavailable'; }).catch((err) => { if (err.name !== 'AbortError') { - usernameState = 'error'; + usernameState.value = 'error'; } }); } function onChangeEmail(): void { - if (email === '') { - emailState = null; + if (email.value === '') { + emailState.value = null; return; } - if (emailAbortController != null) { - emailAbortController.abort(); + if (emailAbortController.value != null) { + emailAbortController.value.abort(); } - emailState = 'wait'; - emailAbortController = new AbortController(); + emailState.value = 'wait'; + emailAbortController.value = new AbortController(); os.api('email-address/available', { - emailAddress: email, - }, undefined, emailAbortController.signal).then(result => { - emailState = result.available ? 'ok' : + emailAddress: email.value, + }, undefined, emailAbortController.value.signal).then(result => { + emailState.value = result.available ? 'ok' : result.reason === 'used' ? 'unavailable:used' : result.reason === 'format' ? 'unavailable:format' : result.reason === 'disposable' ? 'unavailable:disposable' : @@ -215,55 +215,55 @@ function onChangeEmail(): void { 'unavailable'; }).catch((err) => { if (err.name !== 'AbortError') { - emailState = 'error'; + emailState.value = 'error'; } }); } function onChangePassword(): void { - if (password === '') { - passwordStrength = ''; + if (password.value === '') { + passwordStrength.value = ''; return; } - const strength = getPasswordStrength(password); - passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; + const strength = getPasswordStrength(password.value); + passwordStrength.value = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; } function onChangePasswordRetype(): void { - if (retypedPassword === '') { - passwordRetypeState = null; + if (retypedPassword.value === '') { + passwordRetypeState.value = null; return; } - passwordRetypeState = password === retypedPassword ? 'match' : 'not-match'; + passwordRetypeState.value = password.value === retypedPassword.value ? 'match' : 'not-match'; } async function onSubmit(): Promise<void> { - if (submitting) return; - submitting = true; + if (submitting.value) return; + submitting.value = true; try { await os.api('signup', { - username, - password, - emailAddress: email, - invitationCode, - 'hcaptcha-response': hCaptchaResponse, - 'g-recaptcha-response': reCaptchaResponse, - 'turnstile-response': turnstileResponse, + username: username.value, + password: password.value, + emailAddress: email.value, + invitationCode: invitationCode.value, + 'hcaptcha-response': hCaptchaResponse.value, + 'g-recaptcha-response': reCaptchaResponse.value, + 'turnstile-response': turnstileResponse.value, }); if (instance.emailRequiredForSignup) { os.alert({ type: 'success', title: i18n.ts._signup.almostThere, - text: i18n.t('_signup.emailSent', { email }), + text: i18n.t('_signup.emailSent', { email: email.value }), }); emit('signupEmailPending'); } else { const res = await os.api('signin', { - username, - password, + username: username.value, + password: password.value, }); emit('signup', res); @@ -272,10 +272,10 @@ async function onSubmit(): Promise<void> { } } } catch { - submitting = false; - hcaptcha?.reset?.(); - recaptcha?.reset?.(); - turnstile?.reset?.(); + submitting.value = false; + hcaptcha.value?.reset?.(); + recaptcha.value?.reset?.(); + turnstile.value?.reset?.(); os.alert({ type: 'error', diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue index d860ba5fe6..1467049e25 100644 --- a/packages/frontend/src/components/MkSignupDialog.vue +++ b/packages/frontend/src/components/MkSignupDialog.vue @@ -33,8 +33,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; -import { $ref } from 'vue/macros'; +import { shallowRef, ref } from 'vue'; + import XSignup from '@/components/MkSignupDialog.form.vue'; import XServerRules from '@/components/MkSignupDialog.rules.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; @@ -52,17 +52,17 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); -const isAcceptedServerRule = $ref(false); +const isAcceptedServerRule = ref(false); function onSignup(res) { emit('done', res); - dialog.close(); + dialog.value.close(); } function onSignupEmailPending() { - dialog.close(); + dialog.value.close(); } </script> diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 638407872f..370894d4f4 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkMediaList from '@/components/MkMediaList.vue'; import MkPoll from '@/components/MkPoll.vue'; @@ -44,7 +44,7 @@ const props = defineProps<{ const isLong = shouldCollapsed(props.note, []); -const collapsed = $ref(isLong); +const collapsed = ref(isLong); </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkTagCloud.vue b/packages/frontend/src/components/MkTagCloud.vue index a3d82fee5e..083c34906f 100644 --- a/packages/frontend/src/components/MkTagCloud.vue +++ b/packages/frontend/src/components/MkTagCloud.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, watch, onBeforeUnmount } from 'vue'; +import { onMounted, watch, onBeforeUnmount, ref, shallowRef } from 'vue'; import tinycolor from 'tinycolor2'; const loaded = !!window.TagCanvas; @@ -23,13 +23,13 @@ const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz'; const computedStyle = getComputedStyle(document.documentElement); const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); -let available = $ref(false); -let rootEl = $shallowRef<HTMLElement | null>(null); -let canvasEl = $shallowRef<HTMLCanvasElement | null>(null); -let tagsEl = $shallowRef<HTMLElement | null>(null); -let width = $ref(300); +const available = ref(false); +const rootEl = shallowRef<HTMLElement | null>(null); +const canvasEl = shallowRef<HTMLCanvasElement | null>(null); +const tagsEl = shallowRef<HTMLElement | null>(null); +const width = ref(300); -watch($$(available), () => { +watch(available, () => { try { window.TagCanvas.Start(idForCanvas, idForTags, { textColour: '#ffffff', @@ -52,15 +52,15 @@ watch($$(available), () => { }); onMounted(() => { - width = rootEl.offsetWidth; + width.value = rootEl.value.offsetWidth; if (loaded) { - available = true; + available.value = true; } else { document.head.appendChild(Object.assign(document.createElement('script'), { async: true, src: '/client-assets/tagcanvas.min.js', - })).addEventListener('load', () => available = true); + })).addEventListener('load', () => available.value = true); } }); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 04716ebab2..23afb922f3 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch, onUnmounted, provide } from 'vue'; +import { computed, watch, onUnmounted, provide, ref } from 'vue'; import { Connection } from 'misskey-js/built/streaming.js'; import MkNotes from '@/components/MkNotes.vue'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; @@ -62,8 +62,8 @@ type TimelineQueryType = { roleId?: string } -const prComponent: InstanceType<typeof MkPullToRefresh> = $ref(); -const tlComponent: InstanceType<typeof MkNotes> = $ref(); +const prComponent = ref<InstanceType<typeof MkPullToRefresh>>(); +const tlComponent = ref<InstanceType<typeof MkNotes>>(); let tlNotesCount = 0; @@ -74,7 +74,7 @@ const prepend = note => { note._shouldInsertAd_ = true; } - tlComponent.pagingComponent?.prepend(note); + tlComponent.value.pagingComponent?.prepend(note); emit('note'); @@ -248,7 +248,7 @@ function reloadTimeline() { return new Promise<void>((res) => { tlNotesCount = 0; - tlComponent.pagingComponent?.reload().then(() => { + tlComponent.value.pagingComponent?.reload().then(() => { res(); }); }); diff --git a/packages/frontend/src/components/MkToast.vue b/packages/frontend/src/components/MkToast.vue index 48908cf3e6..0446f9196a 100644 --- a/packages/frontend/src/components/MkToast.vue +++ b/packages/frontend/src/components/MkToast.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, ref } from 'vue'; import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; @@ -35,11 +35,11 @@ const emit = defineEmits<{ }>(); const zIndex = os.claimZIndex('high'); -let showing = $ref(true); +const showing = ref(true); onMounted(() => { window.setTimeout(() => { - showing = false; + showing.value = false; }, 4000); }); </script> diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index 8958accc4a..f5fa86a908 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { shallowRef, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkInput from './MkInput.vue'; import MkSwitch from './MkSwitch.vue'; @@ -67,37 +67,37 @@ const emit = defineEmits<{ (ev: 'done', result: { name: string | null, permissions: string[] }): void; }>(); -const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); -let name = $ref(props.initialName); -let permissions = $ref({}); +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); +const name = ref(props.initialName); +const permissions = ref({}); if (props.initialPermissions) { for (const kind of props.initialPermissions) { - permissions[kind] = true; + permissions.value[kind] = true; } } else { for (const kind of Misskey.permissions) { - permissions[kind] = false; + permissions.value[kind] = false; } } function ok(): void { emit('done', { - name: name, - permissions: Object.keys(permissions).filter(p => permissions[p]), + name: name.value, + permissions: Object.keys(permissions.value).filter(p => permissions.value[p]), }); - dialog.close(); + dialog.value.close(); } function disableAll(): void { - for (const p in permissions) { - permissions[p] = false; + for (const p in permissions.value) { + permissions.value[p] = false; } } function enableAll(): void { - for (const p in permissions) { - permissions[p] = true; + for (const p in permissions.value) { + permissions.value[p] = true; } } </script> diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 2332fe9a7c..f0f1a13d0b 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, onUnmounted } from 'vue'; +import { defineAsyncComponent, onUnmounted, ref } from 'vue'; import type { summaly } from 'summaly'; import { url as local } from '@/config.js'; import { i18n } from '@/i18n.js'; @@ -107,36 +107,36 @@ const props = withDefaults(defineProps<{ }); const MOBILE_THRESHOLD = 500; -const isMobile = $ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); +const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); const self = props.url.startsWith(local); const attr = self ? 'to' : 'href'; const target = self ? null : '_blank'; -let fetching = $ref(true); -let title = $ref<string | null>(null); -let description = $ref<string | null>(null); -let thumbnail = $ref<string | null>(null); -let icon = $ref<string | null>(null); -let sitename = $ref<string | null>(null); -let sensitive = $ref<boolean>(false); -let player = $ref({ +const fetching = ref(true); +const title = ref<string | null>(null); +const description = ref<string | null>(null); +const thumbnail = ref<string | null>(null); +const icon = ref<string | null>(null); +const sitename = ref<string | null>(null); +const sensitive = ref<boolean>(false); +const player = ref({ url: null, width: null, height: null, } as SummalyResult['player']); -let playerEnabled = $ref(false); -let tweetId = $ref<string | null>(null); -let tweetExpanded = $ref(props.detail); +const playerEnabled = ref(false); +const tweetId = ref<string | null>(null); +const tweetExpanded = ref(props.detail); const embedId = `embed${Math.random().toString().replace(/\D/, '')}`; -let tweetHeight = $ref(150); -let unknownUrl = $ref(false); +const tweetHeight = ref(150); +const unknownUrl = ref(false); const requestUrl = new URL(props.url); if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com' || requestUrl.hostname === 'x.com' || requestUrl.hostname === 'mobile.x.com') { const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/); - if (m) tweetId = m[1]; + if (m) tweetId.value = m[1]; } if (requestUrl.hostname === 'music.youtube.com' && requestUrl.pathname.match('^/(?:watch|channel)')) { @@ -148,8 +148,8 @@ requestUrl.hash = ''; window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`) .then(res => { if (!res.ok) { - fetching = false; - unknownUrl = true; + fetching.value = false; + unknownUrl.value = true; return; } @@ -157,21 +157,21 @@ window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLa }) .then((info: SummalyResult) => { if (info.url == null) { - fetching = false; - unknownUrl = true; + fetching.value = false; + unknownUrl.value = true; return; } - fetching = false; - unknownUrl = false; + fetching.value = false; + unknownUrl.value = false; - title = info.title; - description = info.description; - thumbnail = info.thumbnail; - icon = info.icon; - sitename = info.sitename; - player = info.player; - sensitive = info.sensitive ?? false; + title.value = info.title; + description.value = info.description; + thumbnail.value = info.thumbnail; + icon.value = info.icon; + sitename.value = info.sitename; + player.value = info.player; + sensitive.value = info.sensitive ?? false; }); function adjustTweetHeight(message: any) { @@ -180,7 +180,7 @@ function adjustTweetHeight(message: any) { if (embed?.method !== 'twttr.private.resize') return; if (embed?.id !== embedId) return; const height = embed?.params[0]?.height; - if (height) tweetHeight = height; + if (height) tweetHeight.value = height; } const openPlayer = (): void => { diff --git a/packages/frontend/src/components/MkUrlPreviewPopup.vue b/packages/frontend/src/components/MkUrlPreviewPopup.vue index 0ab012dfb7..81c383540c 100644 --- a/packages/frontend/src/components/MkUrlPreviewPopup.vue +++ b/packages/frontend/src/components/MkUrlPreviewPopup.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, ref } from 'vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue'; import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; @@ -28,16 +28,16 @@ const emit = defineEmits<{ }>(); const zIndex = os.claimZIndex('middle'); -let top = $ref(0); -let left = $ref(0); +const top = ref(0); +const left = ref(0); onMounted(() => { const rect = props.source.getBoundingClientRect(); const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset; - top = y; - left = x; + top.value = y; + left.value = x; }); </script> diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue index 235df8822f..b9fd084409 100644 --- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue +++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue @@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; @@ -66,12 +66,12 @@ const props = defineProps<{ announcement?: any, }>(); -let dialog = $ref(null); -let title: string = $ref(props.announcement ? props.announcement.title : ''); -let text: string = $ref(props.announcement ? props.announcement.text : ''); -let icon: string = $ref(props.announcement ? props.announcement.icon : 'info'); -let display: string = $ref(props.announcement ? props.announcement.display : 'dialog'); -let needConfirmationToRead = $ref(props.announcement ? props.announcement.needConfirmationToRead : false); +const dialog = ref(null); +const title = ref<string>(props.announcement ? props.announcement.title : ''); +const text = ref<string>(props.announcement ? props.announcement.text : ''); +const icon = ref<string>(props.announcement ? props.announcement.icon : 'info'); +const display = ref<string>(props.announcement ? props.announcement.display : 'dialog'); +const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false); const emit = defineEmits<{ (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, @@ -80,12 +80,12 @@ const emit = defineEmits<{ async function done() { const params = { - title: title, - text: text, - icon: icon, + title: title.value, + text: text.value, + icon: icon.value, imageUrl: null, - display: display, - needConfirmationToRead: needConfirmationToRead, + display: display.value, + needConfirmationToRead: needConfirmationToRead.value, userId: props.user.id, }; @@ -102,7 +102,7 @@ async function done() { }, }); - dialog.close(); + dialog.value.close(); } else { const created = await os.apiWithDialog('admin/announcements/create', params); @@ -110,14 +110,14 @@ async function done() { created: created, }); - dialog.close(); + dialog.value.close(); } } async function del() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: title }), + text: i18n.t('removeAreYouSure', { x: title.value }), }); if (canceled) return; @@ -127,7 +127,7 @@ async function del() { emit('done', { deleted: true, }); - dialog.close(); + dialog.value.close(); }); } </script> diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue index fbc2e09b0b..75288aac02 100644 --- a/packages/frontend/src/components/MkUserCardMini.vue +++ b/packages/frontend/src/components/MkUserCardMini.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import * as Misskey from 'misskey-js'; -import { onMounted } from 'vue'; +import { onMounted, ref } from 'vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; import * as os from '@/os.js'; import { acct } from '@/filters/user.js'; @@ -28,14 +28,14 @@ const props = withDefaults(defineProps<{ withChart: true, }); -let chartValues = $ref<number[] | null>(null); +const chartValues = ref<number[] | null>(null); onMounted(() => { if (props.withChart) { os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く res.inc.splice(0, 1); - chartValues = res.inc; + chartValues.value = res.inc; }); } }); diff --git a/packages/frontend/src/components/MkUserOnlineIndicator.vue b/packages/frontend/src/components/MkUserOnlineIndicator.vue index 8b792fe496..31d6fe6d04 100644 --- a/packages/frontend/src/components/MkUserOnlineIndicator.vue +++ b/packages/frontend/src/components/MkUserOnlineIndicator.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { computed } from 'vue'; import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; @@ -24,7 +24,7 @@ const props = defineProps<{ user: Misskey.entities.User; }>(); -const text = $computed(() => { +const text = computed(() => { switch (props.user.onlineStatus) { case 'online': return i18n.ts.online; case 'active': return i18n.ts.active; diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue index 20eb9b3e93..b703369433 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkFollowButton from '@/components/MkFollowButton.vue'; import { userPage } from '@/filters/user.js'; @@ -80,18 +80,18 @@ const emit = defineEmits<{ }>(); const zIndex = os.claimZIndex('middle'); -let user = $ref<Misskey.entities.UserDetailed | null>(null); -let top = $ref(0); -let left = $ref(0); +const user = ref<Misskey.entities.UserDetailed | null>(null); +const top = ref(0); +const left = ref(0); function showMenu(ev: MouseEvent) { - const { menu, cleanup } = getUserMenu(user); + const { menu, cleanup } = getUserMenu(user.value); os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); } onMounted(() => { if (typeof props.q === 'object') { - user = props.q; + user.value = props.q; } else { const query = props.q.startsWith('@') ? Misskey.acct.parse(props.q.substring(1)) : @@ -99,7 +99,7 @@ onMounted(() => { os.api('users/show', query).then(res => { if (!props.showing) return; - user = res; + user.value = res; }); } @@ -107,8 +107,8 @@ onMounted(() => { const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset; - top = y; - left = x; + top.value = y; + left.value = x; }); </script> diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index ac38c4b62f..9d41147bd2 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkInput from '@/components/MkInput.vue'; import FormSplit from '@/components/form/split.vue'; @@ -78,43 +78,43 @@ const props = defineProps<{ includeSelf?: boolean; }>(); -let username = $ref(''); -let host = $ref(''); -let users: Misskey.entities.UserDetailed[] = $ref([]); -let recentUsers: Misskey.entities.UserDetailed[] = $ref([]); -let selected: Misskey.entities.UserDetailed | null = $ref(null); -let dialogEl = $ref(); +const username = ref(''); +const host = ref(''); +const users = ref<Misskey.entities.UserDetailed[]>([]); +const recentUsers = ref<Misskey.entities.UserDetailed[]>([]); +const selected = ref<Misskey.entities.UserDetailed | null>(null); +const dialogEl = ref(); const search = () => { - if (username === '' && host === '') { - users = []; + if (username.value === '' && host.value === '') { + users.value = []; return; } os.api('users/search-by-username-and-host', { - username: username, - host: host, + username: username.value, + host: host.value, limit: 10, detail: false, }).then(_users => { - users = _users; + users.value = _users; }); }; const ok = () => { - if (selected == null) return; - emit('ok', selected); - dialogEl.close(); + if (selected.value == null) return; + emit('ok', selected.value); + dialogEl.value.close(); // 最近使ったユーザー更新 let recents = defaultStore.state.recentlyUsedUsers; - recents = recents.filter(x => x !== selected.id); - recents.unshift(selected.id); + recents = recents.filter(x => x !== selected.value.id); + recents.unshift(selected.value.id); defaultStore.set('recentlyUsedUsers', recents.splice(0, 16)); }; const cancel = () => { emit('cancel'); - dialogEl.close(); + dialogEl.value.close(); }; onMounted(() => { @@ -122,9 +122,9 @@ onMounted(() => { userIds: defaultStore.state.recentlyUsedUsers, }).then(users => { if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) { - recentUsers = [$i, ...users]; + recentUsers.value = [$i, ...users]; } else { - recentUsers = users; + recentUsers.value = users; } }); }); diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue index 841ab5ba0c..4bca72511d 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue @@ -53,10 +53,10 @@ import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os.js'; import { $i } from '@/account.js'; -let isLocked = ref(false); -let hideOnlineStatus = ref(false); -let noCrawle = ref(false); -let preventAiLearning = ref(true); +const isLocked = ref(false); +const hideOnlineStatus = ref(false); +const noCrawle = ref(false); +const preventAiLearning = ref(true); watch([isLocked, hideOnlineStatus, noCrawle, preventAiLearning], () => { os.api('i/update', { diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue index bbb3d3dbf5..1324ed12e1 100644 --- a/packages/frontend/src/components/MkVisibilityPicker.vue +++ b/packages/frontend/src/components/MkVisibilityPicker.vue @@ -42,12 +42,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { nextTick } from 'vue'; +import { nextTick, shallowRef, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkModal from '@/components/MkModal.vue'; import { i18n } from '@/i18n.js'; -const modal = $shallowRef<InstanceType<typeof MkModal>>(); +const modal = shallowRef<InstanceType<typeof MkModal>>(); const props = withDefaults(defineProps<{ currentVisibility: typeof Misskey.noteVisibilities[number]; @@ -62,13 +62,13 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -let v = $ref(props.currentVisibility); +const v = ref(props.currentVisibility); function choose(visibility: typeof Misskey.noteVisibilities[number]): void { - v = visibility; + v.value = visibility; emit('changeVisibility', visibility); nextTick(() => { - if (modal) modal.close(); + if (modal.value) modal.value.close(); }); } </script> diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue index 26de7dee52..746ed3e0de 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted } from 'vue'; +import { onMounted, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; import tinycolor from 'tinycolor2'; @@ -25,11 +25,11 @@ import { initChart } from '@/scripts/init-chart.js'; initChart(); -const chartEl = $shallowRef<HTMLCanvasElement>(null); +const chartEl = shallowRef<HTMLCanvasElement>(null); const now = new Date(); let chartInstance: Chart = null; const chartLimit = 30; -let fetching = $ref(true); +const fetching = ref(true); const { handler: externalTooltipHandler } = useChartTooltip(); @@ -65,7 +65,7 @@ async function renderChart() { const max = Math.max(...raw.read); - chartInstance = new Chart(chartEl, { + chartInstance = new Chart(chartEl.value, { type: 'bar', data: { datasets: [{ @@ -147,7 +147,7 @@ async function renderChart() { plugins: [chartVLine(vLineColor)], }); - fetching = false; + fetching.value = false; } onMounted(async () => { diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 3eb5c19660..7a41720e3f 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import XTimeline from './welcome.timeline.vue'; import XSigninDialog from '@/components/MkSigninDialog.vue'; @@ -67,15 +67,15 @@ import number from '@/filters/number.js'; import MkNumber from '@/components/MkNumber.vue'; import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue'; -let meta = $ref<Misskey.entities.MetaResponse | null>(null); -let stats = $ref<Misskey.entities.StatsResponse | null>(null); +const meta = ref<Misskey.entities.MetaResponse | null>(null); +const stats = ref<Misskey.entities.StatsResponse | null>(null); os.api('meta', { detail: true }).then(_meta => { - meta = _meta; + meta.value = _meta; }); os.api('stats', {}).then((res) => { - stats = res; + stats.value = res; }); function signin() { diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index ccb8b09b6c..1150a29e03 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onBeforeUnmount, onMounted, provide } from 'vue'; +import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue'; import contains from '@/scripts/contains.js'; import * as os from '@/os.js'; import { MenuItem } from '@/types/menu'; @@ -107,18 +107,18 @@ const emit = defineEmits<{ provide('inWindow', true); -let rootEl = $shallowRef<HTMLElement | null>(); -let showing = $ref(true); +const rootEl = shallowRef<HTMLElement | null>(); +const showing = ref(true); let beforeClickedAt = 0; -let maximized = $ref(false); -let minimized = $ref(false); +const maximized = ref(false); +const minimized = ref(false); let unResizedTop = ''; let unResizedLeft = ''; let unResizedWidth = ''; let unResizedHeight = ''; function close() { - showing = false; + showing.value = false; } function onKeydown(evt) { @@ -137,46 +137,46 @@ function onContextmenu(ev: MouseEvent) { // 最前面へ移動 function top() { - if (rootEl) { - rootEl.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low'); + if (rootEl.value) { + rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low'); } } function maximize() { - maximized = true; - unResizedTop = rootEl.style.top; - unResizedLeft = rootEl.style.left; - unResizedWidth = rootEl.style.width; - unResizedHeight = rootEl.style.height; - rootEl.style.top = '0'; - rootEl.style.left = '0'; - rootEl.style.width = '100%'; - rootEl.style.height = '100%'; + maximized.value = true; + unResizedTop = rootEl.value.style.top; + unResizedLeft = rootEl.value.style.left; + unResizedWidth = rootEl.value.style.width; + unResizedHeight = rootEl.value.style.height; + rootEl.value.style.top = '0'; + rootEl.value.style.left = '0'; + rootEl.value.style.width = '100%'; + rootEl.value.style.height = '100%'; } function unMaximize() { - maximized = false; - rootEl.style.top = unResizedTop; - rootEl.style.left = unResizedLeft; - rootEl.style.width = unResizedWidth; - rootEl.style.height = unResizedHeight; + maximized.value = false; + rootEl.value.style.top = unResizedTop; + rootEl.value.style.left = unResizedLeft; + rootEl.value.style.width = unResizedWidth; + rootEl.value.style.height = unResizedHeight; } function minimize() { - minimized = true; - unResizedWidth = rootEl.style.width; - unResizedHeight = rootEl.style.height; - rootEl.style.width = minWidth + 'px'; - rootEl.style.height = props.mini ? '32px' : '39px'; + minimized.value = true; + unResizedWidth = rootEl.value.style.width; + unResizedHeight = rootEl.value.style.height; + rootEl.value.style.width = minWidth + 'px'; + rootEl.value.style.height = props.mini ? '32px' : '39px'; } function unMinimize() { - const main = rootEl; + const main = rootEl.value; if (main == null) return; - minimized = false; - rootEl.style.width = unResizedWidth; - rootEl.style.height = unResizedHeight; + minimized.value = false; + rootEl.value.style.width = unResizedWidth; + rootEl.value.style.height = unResizedHeight; const browserWidth = window.innerWidth; const browserHeight = window.innerHeight; const windowWidth = main.offsetWidth; @@ -192,7 +192,7 @@ function onBodyMousedown() { } function onDblClick() { - if (minimized) { + if (minimized.value) { unMinimize(); } else { maximize(); @@ -205,7 +205,7 @@ function onHeaderMousedown(evt: MouseEvent) { let beforeMaximized = false; - if (maximized) { + if (maximized.value) { beforeMaximized = true; unMaximize(); } @@ -219,7 +219,7 @@ function onHeaderMousedown(evt: MouseEvent) { beforeClickedAt = Date.now(); - const main = rootEl; + const main = rootEl.value; if (main == null) return; if (!contains(main, document.activeElement)) main.focus(); @@ -251,8 +251,8 @@ function onHeaderMousedown(evt: MouseEvent) { // 右はみ出し if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth; - rootEl.style.left = moveLeft + 'px'; - rootEl.style.top = moveTop + 'px'; + rootEl.value.style.left = moveLeft + 'px'; + rootEl.value.style.top = moveTop + 'px'; } if (beforeMaximized) { @@ -270,7 +270,7 @@ function onHeaderMousedown(evt: MouseEvent) { // 上ハンドル掴み時 function onTopHandleMousedown(evt) { - const main = rootEl; + const main = rootEl.value; // どういうわけかnullになることがある if (main == null) return; @@ -298,7 +298,7 @@ function onTopHandleMousedown(evt) { // 右ハンドル掴み時 function onRightHandleMousedown(evt) { - const main = rootEl; + const main = rootEl.value; if (main == null) return; const base = evt.clientX; @@ -323,7 +323,7 @@ function onRightHandleMousedown(evt) { // 下ハンドル掴み時 function onBottomHandleMousedown(evt) { - const main = rootEl; + const main = rootEl.value; if (main == null) return; const base = evt.clientY; @@ -348,7 +348,7 @@ function onBottomHandleMousedown(evt) { // 左ハンドル掴み時 function onLeftHandleMousedown(evt) { - const main = rootEl; + const main = rootEl.value; if (main == null) return; const base = evt.clientX; @@ -400,27 +400,27 @@ function onBottomLeftHandleMousedown(evt) { // 高さを適用 function applyTransformHeight(height) { if (height > window.innerHeight) height = window.innerHeight; - rootEl.style.height = height + 'px'; + rootEl.value.style.height = height + 'px'; } // 幅を適用 function applyTransformWidth(width) { if (width > window.innerWidth) width = window.innerWidth; - rootEl.style.width = width + 'px'; + rootEl.value.style.width = width + 'px'; } // Y座標を適用 function applyTransformTop(top) { - rootEl.style.top = top + 'px'; + rootEl.value.style.top = top + 'px'; } // X座標を適用 function applyTransformLeft(left) { - rootEl.style.left = left + 'px'; + rootEl.value.style.left = left + 'px'; } function onBrowserResize() { - const main = rootEl; + const main = rootEl.value; if (main == null) return; const position = main.getBoundingClientRect(); @@ -438,8 +438,8 @@ onMounted(() => { applyTransformWidth(props.initialWidth); if (props.initialHeight) applyTransformHeight(props.initialHeight); - applyTransformTop((window.innerHeight / 2) - (rootEl.offsetHeight / 2)); - applyTransformLeft((window.innerWidth / 2) - (rootEl.offsetWidth / 2)); + applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2)); + applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2)); // 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする top(); diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue index d74ad0eda4..c6b18aeceb 100644 --- a/packages/frontend/src/components/MkYouTubePlayer.vue +++ b/packages/frontend/src/components/MkYouTubePlayer.vue @@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { ref } from 'vue'; import MkWindow from '@/components/MkWindow.vue'; import { versatileLang } from '@/scripts/intl-const.js'; import { defaultStore } from '@/store.js'; @@ -35,22 +36,22 @@ const props = defineProps<{ const requestUrl = new URL(props.url); if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); -let fetching = $ref(true); -let title = $ref<string | null>(null); -let player = $ref({ +const fetching = ref(true); +const title = ref<string | null>(null); +const player = ref({ url: null, width: null, height: null, }); const ytFetch = (): void => { - fetching = true; + fetching.value = true; window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`).then(res => { res.json().then(info => { if (info.url == null) return; - title = info.title; - fetching = false; - player = info.player; + title.value = info.title; + fetching.value = false; + player.value = info.player; }); }); }; diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue index 2c50511b8b..008d10f8eb 100644 --- a/packages/frontend/src/components/global/MkA.vue +++ b/packages/frontend/src/components/global/MkA.vue @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { computed } from 'vue'; import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { url } from '@/config.js'; @@ -28,7 +29,7 @@ const props = withDefaults(defineProps<{ const router = useRouter(); -const active = $computed(() => { +const active = computed(() => { if (props.activeClass == null) return false; const resolved = router.resolve(props.to); if (resolved == null) return false; diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue index 421fe99127..3ef5db3fe3 100644 --- a/packages/frontend/src/components/global/MkAd.vue +++ b/packages/frontend/src/components/global/MkAd.vue @@ -96,7 +96,7 @@ const choseAd = (): Ad | null => { }; const chosen = ref(choseAd()); -const shouldHide = $ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null)); +const shouldHide = ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null)); function reduceFrequency(): void { if (chosen.value == null) return; diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 51a454b2cc..c7e50e275a 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch } from 'vue'; +import { watch, ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import MkImgWithBlurhash from '../MkImgWithBlurhash.vue'; import MkA from './MkA.vue'; @@ -47,9 +47,9 @@ import { acct, userPage } from '@/filters/user.js'; import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue'; import { defaultStore } from '@/store.js'; -const animation = $ref(defaultStore.state.animation); -const squareAvatars = $ref(defaultStore.state.squareAvatars); -const useBlurEffect = $ref(defaultStore.state.useBlurEffect); +const animation = ref(defaultStore.state.animation); +const squareAvatars = ref(defaultStore.state.squareAvatars); +const useBlurEffect = ref(defaultStore.state.useBlurEffect); const props = withDefaults(defineProps<{ user: Misskey.entities.User; @@ -79,11 +79,11 @@ const emit = defineEmits<{ const showDecoration = props.forceShowDecoration || defaultStore.state.showAvatarDecorations; -const bound = $computed(() => props.link +const bound = computed(() => props.link ? { to: userPage(props.user), target: props.target } : {}); -const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) +const url = computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) ? getStaticImageUrl(props.user.avatarUrl) : props.user.avatarUrl); @@ -116,10 +116,10 @@ function getDecorationScale() { return scaleX === 1 ? undefined : `${scaleX} 1`; } -let color = $ref<string | undefined>(); +const color = ref<string | undefined>(); watch(() => props.user.avatarBlurhash, () => { - color = extractAvgColorFromBlurhash(props.user.avatarBlurhash); + color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash); }, { immediate: true, }); diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index 1e17bab849..a092497307 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, inject } from 'vue'; +import { computed, inject, ref } from 'vue'; import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js'; import { defaultStore } from '@/store.js'; import { customEmojisMap } from '@/custom-emojis.js'; @@ -71,7 +71,7 @@ const url = computed(() => { }); const alt = computed(() => `:${customEmojiName.value}:`); -let errored = $ref(url.value == null); +const errored = ref(url.value == null); function onClick(ev: MouseEvent) { if (props.menu) { diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 935ca33eb5..301e691fa0 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref, inject } from 'vue'; +import { onMounted, onUnmounted, ref, inject, shallowRef, computed } from 'vue'; import tinycolor from 'tinycolor2'; import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import { scrollToTop } from '@/scripts/scroll.js'; @@ -69,13 +69,13 @@ const metadata = injectPageMetadata(); const hideTitle = inject('shouldOmitHeaderTitle', false); const thin_ = props.thin || inject('shouldHeaderThin', false); -let el = $shallowRef<HTMLElement | undefined>(undefined); +const el = shallowRef<HTMLElement | undefined>(undefined); const bg = ref<string | undefined>(undefined); -let narrow = $ref(false); -const hasTabs = $computed(() => props.tabs.length > 0); -const hasActions = $computed(() => props.actions && props.actions.length > 0); -const show = $computed(() => { - return !hideTitle || hasTabs || hasActions; +const narrow = ref(false); +const hasTabs = computed(() => props.tabs.length > 0); +const hasActions = computed(() => props.actions && props.actions.length > 0); +const show = computed(() => { + return !hideTitle || hasTabs.value || hasActions.value; }); const preventDrag = (ev: TouchEvent) => { @@ -83,8 +83,8 @@ const preventDrag = (ev: TouchEvent) => { }; const top = () => { - if (el) { - scrollToTop(el as HTMLElement, { behavior: 'smooth' }); + if (el.value) { + scrollToTop(el.value as HTMLElement, { behavior: 'smooth' }); } }; @@ -111,14 +111,14 @@ onMounted(() => { calcBg(); globalEvents.on('themeChanged', calcBg); - if (el && el.parentElement) { - narrow = el.parentElement.offsetWidth < 500; + if (el.value && el.value.parentElement) { + narrow.value = el.value.parentElement.offsetWidth < 500; ro = new ResizeObserver((entries, observer) => { - if (el && el.parentElement && document.body.contains(el as HTMLElement)) { - narrow = el.parentElement.offsetWidth < 500; + if (el.value && el.value.parentElement && document.body.contains(el.value as HTMLElement)) { + narrow.value = el.value.parentElement.offsetWidth < 500; } }); - ro.observe(el.parentElement as HTMLElement); + ro.observe(el.value.parentElement as HTMLElement); } }); diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue index 8e9bff11d1..1d707af2d1 100644 --- a/packages/frontend/src/components/global/MkStickyContainer.vue +++ b/packages/frontend/src/components/global/MkStickyContainer.vue @@ -18,36 +18,36 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue'; -import { $$ } from 'vue/macros'; +import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue'; + import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const'; -const rootEl = $shallowRef<HTMLElement>(); -const headerEl = $shallowRef<HTMLElement>(); -const footerEl = $shallowRef<HTMLElement>(); -const bodyEl = $shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); +const headerEl = shallowRef<HTMLElement>(); +const footerEl = shallowRef<HTMLElement>(); +const bodyEl = shallowRef<HTMLElement>(); -let headerHeight = $ref<string | undefined>(); -let childStickyTop = $ref(0); +const headerHeight = ref<string | undefined>(); +const childStickyTop = ref(0); const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0)); -provide(CURRENT_STICKY_TOP, $$(childStickyTop)); +provide(CURRENT_STICKY_TOP, childStickyTop); -let footerHeight = $ref<string | undefined>(); -let childStickyBottom = $ref(0); +const footerHeight = ref<string | undefined>(); +const childStickyBottom = ref(0); const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0)); -provide(CURRENT_STICKY_BOTTOM, $$(childStickyBottom)); +provide(CURRENT_STICKY_BOTTOM, childStickyBottom); const calc = () => { // コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる - if (headerEl != null) { - childStickyTop = parentStickyTop.value + headerEl.offsetHeight; - headerHeight = headerEl.offsetHeight.toString(); + if (headerEl.value != null) { + childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight; + headerHeight.value = headerEl.value.offsetHeight.toString(); } // コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる - if (footerEl != null) { - childStickyBottom = parentStickyBottom.value + footerEl.offsetHeight; - footerHeight = footerEl.offsetHeight.toString(); + if (footerEl.value != null) { + childStickyBottom.value = parentStickyBottom.value + footerEl.value.offsetHeight; + footerHeight.value = footerEl.value.offsetHeight.toString(); } }; @@ -62,28 +62,28 @@ onMounted(() => { watch([parentStickyTop, parentStickyBottom], calc); - watch($$(childStickyTop), () => { - bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`); + watch(childStickyTop, () => { + bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`); }, { immediate: true, }); - watch($$(childStickyBottom), () => { - bodyEl.style.setProperty('--stickyBottom', `${childStickyBottom}px`); + watch(childStickyBottom, () => { + bodyEl.value.style.setProperty('--stickyBottom', `${childStickyBottom.value}px`); }, { immediate: true, }); - headerEl.style.position = 'sticky'; - headerEl.style.top = 'var(--stickyTop, 0)'; - headerEl.style.zIndex = '1000'; + headerEl.value.style.position = 'sticky'; + headerEl.value.style.top = 'var(--stickyTop, 0)'; + headerEl.value.style.zIndex = '1000'; - footerEl.style.position = 'sticky'; - footerEl.style.bottom = 'var(--stickyBottom, 0)'; - footerEl.style.zIndex = '1000'; + footerEl.value.style.position = 'sticky'; + footerEl.value.style.bottom = 'var(--stickyBottom, 0)'; + footerEl.value.style.zIndex = '1000'; - observer.observe(headerEl); - observer.observe(footerEl); + observer.observe(headerEl.value); + observer.observe(footerEl.value); }); onUnmounted(() => { @@ -91,6 +91,6 @@ onUnmounted(() => { }); defineExpose({ - rootEl: $$(rootEl), + rootEl: rootEl, }); </script> diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue index 2eeab4d284..e11db9dc31 100644 --- a/packages/frontend/src/components/global/MkTime.vue +++ b/packages/frontend/src/components/global/MkTime.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import isChromatic from 'chromatic/isChromatic'; -import { onMounted, onUnmounted } from 'vue'; +import { onMounted, onUnmounted, ref, computed } from 'vue'; import { i18n } from '@/i18n.js'; import { dateTimeFormat } from '@/scripts/intl-const.js'; @@ -47,29 +47,29 @@ const invalid = Number.isNaN(_time); const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid; // eslint-disable-next-line vue/no-setup-props-destructure -let now = $ref((props.origin ?? new Date()).getTime()); -const ago = $computed(() => (now - _time) / 1000/*ms*/); +const now = ref((props.origin ?? new Date()).getTime()); +const ago = computed(() => (now.value - _time) / 1000/*ms*/); -const relative = $computed<string>(() => { +const relative = computed<string>(() => { if (props.mode === 'absolute') return ''; // absoluteではrelativeを使わないので計算しない if (invalid) return i18n.ts._ago.invalid; return ( - ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) : - ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) : - ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) : - ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) : - ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) : - ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) : - ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) : - ago >= -3 ? i18n.ts._ago.justNow : - ago < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago / 31536000).toString() }) : - ago < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago / 2592000).toString() }) : - ago < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago / 604800).toString() }) : - ago < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago / 86400).toString() }) : - ago < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago / 3600).toString() }) : - ago < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago / 60)).toString() }) : - i18n.t('_timeIn.seconds', { n: (~~(-ago % 60)).toString() }) + ago.value >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago.value / 31536000).toString() }) : + ago.value >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago.value / 2592000).toString() }) : + ago.value >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago.value / 604800).toString() }) : + ago.value >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago.value / 86400).toString() }) : + ago.value >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago.value / 3600).toString() }) : + ago.value >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago.value / 60)).toString() }) : + ago.value >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago.value % 60)).toString() }) : + ago.value >= -3 ? i18n.ts._ago.justNow : + ago.value < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago.value / 31536000).toString() }) : + ago.value < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago.value / 2592000).toString() }) : + ago.value < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago.value / 604800).toString() }) : + ago.value < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago.value / 86400).toString() }) : + ago.value < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago.value / 3600).toString() }) : + ago.value < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago.value / 60)).toString() }) : + i18n.t('_timeIn.seconds', { n: (~~(-ago.value % 60)).toString() }) ); }); @@ -77,8 +77,8 @@ let tickId: number; let currentInterval: number; function tick() { - now = (new Date()).getTime(); - const nextInterval = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000; + now.value = (new Date()).getTime(); + const nextInterval = ago.value < 60 ? 10000 : ago.value < 3600 ? 60000 : 180000; if (currentInterval !== nextInterval) { if (tickId) window.clearInterval(tickId); diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 99f42f4fcb..9da8f8c379 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { inject, onBeforeUnmount, provide } from 'vue'; +import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue'; import { Resolved, Router } from '@/nirax'; import { defaultStore } from '@/store.js'; @@ -46,16 +46,16 @@ function resolveNested(current: Resolved, d = 0): Resolved | null { } const current = resolveNested(router.current)!; -let currentPageComponent = $shallowRef(current.route.component); -let currentPageProps = $ref(current.props); -let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props))); +const currentPageComponent = shallowRef(current.route.component); +const currentPageProps = ref(current.props); +const key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props))); function onChange({ resolved, key: newKey }) { const current = resolveNested(resolved); if (current == null) return; - currentPageComponent = current.route.component; - currentPageProps = current.props; - key = current.route.path + JSON.stringify(Object.fromEntries(current.props)); + currentPageComponent.value = current.route.component; + currentPageProps.value = current.props; + key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props)); } router.addListener('change', onChange); |