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: 対応漏れ
This commit is contained in:
zyoshoka 2023-12-07 14:42:09 +09:00 committed by GitHub
parent e42c91dee7
commit 406b4bdbe7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
277 changed files with 3353 additions and 3441 deletions

View File

@ -69,12 +69,6 @@ module.exports = {
'require': false, 'require': false,
'__dirname': false, '__dirname': false,
// Vue
'$$': false,
'$ref': false,
'$shallowRef': false,
'$computed': false,
// Misskey // Misskey
'_DEV_': false, '_DEV_': false,
'_LANGS_': false, '_LANGS_': false,

View File

@ -25,7 +25,6 @@
"@syuilo/aiscript": "0.16.0", "@syuilo/aiscript": "0.16.0",
"@tabler/icons-webfont": "2.37.0", "@tabler/icons-webfont": "2.37.0",
"@vitejs/plugin-vue": "4.5.1", "@vitejs/plugin-vue": "4.5.1",
"@vue-macros/reactivity-transform": "0.4.0",
"@vue/compiler-sfc": "3.3.9", "@vue/compiler-sfc": "3.3.9",
"astring": "1.8.6", "astring": "1.8.6",
"autosize": "6.0.1", "autosize": "6.0.1",

View File

@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import MkKeyValue from '@/components/MkKeyValue.vue'; import MkKeyValue from '@/components/MkKeyValue.vue';
@ -56,11 +57,11 @@ const emit = defineEmits<{
(ev: 'resolved', reportId: string): void; (ev: 'resolved', reportId: string): void;
}>(); }>();
let forward = $ref(props.report.forwarded); const forward = ref(props.report.forwarded);
function resolve() { function resolve() {
os.apiWithDialog('admin/resolve-abuse-user-report', { os.apiWithDialog('admin/resolve-abuse-user-report', {
forward: forward, forward: forward.value,
reportId: props.report.id, reportId: props.report.id,
}).then(() => { }).then(() => {
emit('resolved', props.report.id); emit('resolved', props.report.id);

View File

@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { onMounted } from 'vue'; import { onMounted, ref, computed } from 'vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js'; import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
@ -67,15 +67,15 @@ const props = withDefaults(defineProps<{
withDescription: true, withDescription: true,
}); });
let achievements = $ref(); const achievements = ref();
const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x))); const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
function fetch() { function fetch() {
os.api('users/achievements', { userId: props.user.id }).then(res => { os.api('users/achievements', { userId: props.user.id }).then(res => {
achievements = []; achievements.value = [];
for (const t of ACHIEVEMENT_TYPES) { for (const t of ACHIEVEMENT_TYPES) {
const a = res.find(x => x.name === t); 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); //achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt);
}); });

View File

@ -138,45 +138,45 @@ const texts = computed(() => {
}); });
let enabled = true; let enabled = true;
let majorGraduationColor = $ref<string>(); const majorGraduationColor = ref<string>();
//let minorGraduationColor = $ref<string>(); //let minorGraduationColor = $ref<string>();
let sHandColor = $ref<string>(); const sHandColor = ref<string>();
let mHandColor = $ref<string>(); const mHandColor = ref<string>();
let hHandColor = $ref<string>(); const hHandColor = ref<string>();
let nowColor = $ref<string>(); const nowColor = ref<string>();
let h = $ref<number>(0); const h = ref<number>(0);
let m = $ref<number>(0); const m = ref<number>(0);
let s = $ref<number>(0); const s = ref<number>(0);
let hAngle = $ref<number>(0); const hAngle = ref<number>(0);
let mAngle = $ref<number>(0); const mAngle = ref<number>(0);
let sAngle = $ref<number>(0); const sAngle = ref<number>(0);
let disableSAnimate = $ref(false); const disableSAnimate = ref(false);
let sOneRound = false; let sOneRound = false;
const sLine = ref<SVGPathElement>(); const sLine = ref<SVGPathElement>();
function tick() { function tick() {
const now = props.now(); const now = props.now();
now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset); now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
const previousS = s; const previousS = s.value;
const previousM = m; const previousM = m.value;
const previousH = h; const previousH = h.value;
s = now.getSeconds(); s.value = now.getSeconds();
m = now.getMinutes(); m.value = now.getMinutes();
h = now.getHours(); h.value = now.getHours();
if (previousS === s && previousM === m && previousH === h) { if (previousS === s.value && previousM === m.value && previousH === h.value) {
return; return;
} }
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6); hAngle.value = Math.PI * (h.value % (props.twentyfour ? 24 : 12) + (m.value + s.value / 60) / 60) / (props.twentyfour ? 12 : 6);
mAngle = Math.PI * (m + s / 60) / 30; mAngle.value = Math.PI * (m.value + s.value / 60) / 30;
if (sOneRound && sLine.value) { // (59->0) if (sOneRound && sLine.value) { // (59->0)
sAngle = Math.PI * 60 / 30; sAngle.value = Math.PI * 60 / 30;
defaultIdlingRenderScheduler.delete(tick); defaultIdlingRenderScheduler.delete(tick);
sLine.value.addEventListener('transitionend', () => { sLine.value.addEventListener('transitionend', () => {
disableSAnimate = true; disableSAnimate.value = true;
requestAnimationFrame(() => { requestAnimationFrame(() => {
sAngle = 0; sAngle.value = 0;
requestAnimationFrame(() => { requestAnimationFrame(() => {
disableSAnimate = false; disableSAnimate.value = false;
if (enabled) { if (enabled) {
defaultIdlingRenderScheduler.add(tick); defaultIdlingRenderScheduler.add(tick);
} }
@ -184,9 +184,9 @@ function tick() {
}); });
}, { once: true }); }, { once: true });
} else { } else {
sAngle = Math.PI * s / 30; sAngle.value = Math.PI * s.value / 30;
} }
sOneRound = s === 59; sOneRound = s.value === 59;
} }
tick(); tick();
@ -195,12 +195,12 @@ function calcColors() {
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(document.documentElement);
const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark(); const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString(); 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)'; //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)'; sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
mHandColor = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString(); mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
hHandColor = accent; hHandColor.value = accent;
nowColor = accent; nowColor.value = accent;
} }
calcColors(); calcColors();

View File

@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Ref } from 'vue'; import { Ref, ref } from 'vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
@ -87,10 +87,10 @@ function g(id) {
return props.components.find(x => x.value.id === id).value; 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) { function onSwitchUpdate(v) {
valueForSwitch = v; valueForSwitch.value = v;
if (c.onChange) c.onChange(v); if (c.onChange) c.onChange(v);
} }

View File

@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted } from 'vue'; import { nextTick, onMounted, shallowRef } from 'vue';
const props = defineProps<{ const props = defineProps<{
type?: 'button' | 'submit' | 'reset'; type?: 'button' | 'submit' | 'reset';
@ -59,13 +59,13 @@ const emit = defineEmits<{
(ev: 'click', payload: MouseEvent): void; (ev: 'click', payload: MouseEvent): void;
}>(); }>();
let el = $shallowRef<HTMLElement | null>(null); const el = shallowRef<HTMLElement | null>(null);
let ripples = $shallowRef<HTMLElement | null>(null); const ripples = shallowRef<HTMLElement | null>(null);
onMounted(() => { onMounted(() => {
if (props.autofocus) { if (props.autofocus) {
nextTick(() => { nextTick(() => {
el!.focus(); el.value!.focus();
}); });
} }
}); });
@ -88,11 +88,11 @@ function onMousedown(evt: MouseEvent): void {
const rect = target.getBoundingClientRect(); const rect = target.getBoundingClientRect();
const ripple = document.createElement('div'); 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.top = (evt.clientY - rect.top - 1).toString() + 'px';
ripple.style.left = (evt.clientX - rect.left - 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 circleCenterX = evt.clientX - rect.left;
const circleCenterY = evt.clientY - rect.top; const circleCenterY = evt.clientY - rect.top;
@ -107,7 +107,7 @@ function onMousedown(evt: MouseEvent): void {
ripple.style.opacity = '0'; ripple.style.opacity = '0';
}, 1000); }, 1000);
window.setTimeout(() => { window.setTimeout(() => {
if (ripples) ripples.removeChild(ripple); if (ripples.value) ripples.value.removeChild(ripple);
}, 2000); }, 2000);
} }
</script> </script>

View File

@ -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 sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
const negate = arr => arr.map(x => -x); const negate = arr => arr.map(x => -x);
@ -268,7 +268,7 @@ const render = () => {
gradient, gradient,
}, },
}, },
plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl)] : [])], plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl.value)] : [])],
}); });
}; };

View File

@ -13,29 +13,30 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue';
import { Chart, LegendItem } from 'chart.js'; import { Chart, LegendItem } from 'chart.js';
const props = defineProps({ const props = defineProps({
}); });
let chart = $shallowRef<Chart>(); const chart = shallowRef<Chart>();
let items = $shallowRef<LegendItem[]>([]); const items = shallowRef<LegendItem[]>([]);
function update(_chart: Chart, _items: LegendItem[]) { function update(_chart: Chart, _items: LegendItem[]) {
chart = _chart, chart.value = _chart,
items = _items; items.value = _items;
} }
function onClick(item: LegendItem) { function onClick(item: LegendItem) {
if (chart == null) return; if (chart.value == null) return;
const { type } = chart.config; const { type } = chart.value.config;
if (type === 'pie' || type === 'doughnut') { if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item // Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index); chart.value.toggleDataVisibility(item.index);
} else { } 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({ defineExpose({

View File

@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, onUnmounted } from 'vue'; import { computed, onMounted, onUnmounted, ref } from 'vue';
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue'; import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { useInterval } from '@/scripts/use-interval.js'; import { useInterval } from '@/scripts/use-interval.js';
@ -29,8 +29,8 @@ import { claimAchievement } from '@/scripts/achievements.js';
const saveData = game.saveData; const saveData = game.saveData;
const cookies = computed(() => saveData.value?.cookies); const cookies = computed(() => saveData.value?.cookies);
let cps = $ref(0); const cps = ref(0);
let prevCookies = $ref(0); const prevCookies = ref(0);
function onClick(ev: MouseEvent) { function onClick(ev: MouseEvent) {
const x = ev.clientX; const x = ev.clientX;
@ -48,9 +48,9 @@ function onClick(ev: MouseEvent) {
} }
useInterval(() => { useInterval(() => {
const diff = saveData.value!.cookies - prevCookies; const diff = saveData.value!.cookies - prevCookies.value;
cps = diff; cps.value = diff;
prevCookies = saveData.value!.cookies; prevCookies.value = saveData.value!.cookies;
}, 1000, { }, 1000, {
immediate: false, immediate: false,
afterMounted: true, afterMounted: true,
@ -63,7 +63,7 @@ useInterval(game.save, 1000 * 5, {
onMounted(async () => { onMounted(async () => {
await game.load(); await game.load();
prevCookies = saveData.value!.cookies; prevCookies.value = saveData.value!.cookies;
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onBeforeUnmount } from 'vue'; import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import { MenuItem } from './types/menu.vue'; import { MenuItem } from './types/menu.vue';
import contains from '@/scripts/contains.js'; import contains from '@/scripts/contains.js';
@ -34,9 +34,9 @@ const emit = defineEmits<{
(ev: 'closed'): void; (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; const SCROLLBAR_THICKNESS = 16;
@ -44,8 +44,8 @@ onMounted(() => {
let left = props.ev.pageX + 1; // + 1 let left = props.ev.pageX + 1; // + 1
let top = props.ev.pageY + 1; // + 1 let top = props.ev.pageY + 1; // + 1
const width = rootEl.offsetWidth; const width = rootEl.value.offsetWidth;
const height = rootEl.offsetHeight; const height = rootEl.value.offsetHeight;
if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) { if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset; left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
@ -63,8 +63,8 @@ onMounted(() => {
left = 0; left = 0;
} }
rootEl.style.top = `${top}px`; rootEl.value.style.top = `${top}px`;
rootEl.style.left = `${left}px`; rootEl.value.style.left = `${left}px`;
document.body.addEventListener('mousedown', onMousedown); document.body.addEventListener('mousedown', onMousedown);
}); });
@ -74,7 +74,7 @@ onBeforeUnmount(() => {
}); });
function onMousedown(evt: Event) { 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> </script>

View File

@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, shallowRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import Cropper from 'cropperjs'; import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
@ -56,10 +56,10 @@ const props = defineProps<{
}>(); }>();
const imgUrl = getProxiedImageUrl(props.file.url, undefined, true); const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
let dialogEl = $shallowRef<InstanceType<typeof MkModalWindow>>(); const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
let imgEl = $shallowRef<HTMLImageElement>(); const imgEl = shallowRef<HTMLImageElement>();
let cropper: Cropper | null = null; let cropper: Cropper | null = null;
let loading = $ref(true); const loading = ref(true);
const ok = async () => { const ok = async () => {
const promise = new Promise<Misskey.entities.DriveFile>(async (res) => { const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
@ -94,16 +94,16 @@ const ok = async () => {
const f = await promise; const f = await promise;
emit('ok', f); emit('ok', f);
dialogEl!.close(); dialogEl.value!.close();
}; };
const cancel = () => { const cancel = () => {
emit('cancel'); emit('cancel');
dialogEl!.close(); dialogEl.value!.close();
}; };
const onImageLoad = () => { const onImageLoad = () => {
loading = false; loading.value = false;
if (cropper) { if (cropper) {
cropper.getCropperImage()!.$center('contain'); cropper.getCropperImage()!.$center('contain');
@ -112,7 +112,7 @@ const onImageLoad = () => {
}; };
onMounted(() => { onMounted(() => {
cropper = new Cropper(imgEl!, { cropper = new Cropper(imgEl.value!, {
}); });
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(document.documentElement);

View File

@ -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"> <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 v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
<template #caption> <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-if="okButtonDisabledReason === '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-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
</template> </template>
</MkInput> </MkInput>
<MkSelect v-if="select" v-model="selectedValue" autofocus> <MkSelect v-if="select" v-model="selectedValue" autofocus>
@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
</MkSelect> </MkSelect>
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> <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> <MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
</div> </div>
<div v-if="actions" :class="$style.buttons"> <div v-if="actions" :class="$style.buttons">
@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.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 inputValue = ref<string | number | null>(props.input?.default ?? null);
const selectedValue = ref(props.select?.default ?? null); const selectedValue = ref(props.select?.default ?? null);
let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null); const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
const okButtonDisabled = $computed<boolean>(() => {
if (props.input) { if (props.input) {
if (props.input.minLength) { if (props.input.minLength) {
if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) { if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
disabledReason = 'charactersBelow'; return 'charactersBelow';
return true;
} }
} }
if (props.input.maxLength) { if (props.input.maxLength) {
if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) { if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) {
disabledReason = 'charactersExceeded'; return 'charactersExceeded';
return true;
} }
} }
} }
return false; return null;
}); });
function done(canceled: boolean, result?) { function done(canceled: boolean, result?) {

View File

@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -54,23 +55,23 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const modal = $shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>(); const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>();
function chosen(emoji: any) { function chosen(emoji: any) {
emit('done', emoji); emit('done', emoji);
if (props.choseAndClose) { if (props.choseAndClose) {
modal?.close(); modal.value?.close();
} }
} }
function opening() { function opening() {
picker?.reset(); picker.value?.reset();
picker?.focus(); picker.value?.focus();
// //
setTimeout(() => { setTimeout(() => {
picker?.focus(); picker.value?.focus();
}, 10); }, 10);
} }
</script> </script>

View File

@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { shallowRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
@ -42,12 +42,12 @@ const emit = defineEmits<{
(ev: 'closed'): void; (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() { async function ok() {
emit('done', caption); emit('done', caption.value);
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted } from 'vue'; import { nextTick, onMounted, shallowRef, ref } from 'vue';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -70,10 +70,10 @@ const getBgColor = (el: HTMLElement) => {
} }
}; };
let rootEl = $shallowRef<HTMLElement>(); const rootEl = shallowRef<HTMLElement>();
let bgSame = $ref(false); const bgSame = ref(false);
let opened = $ref(props.defaultOpen); const opened = ref(props.defaultOpen);
let openedAtLeastOnce = $ref(props.defaultOpen); const openedAtLeastOnce = ref(props.defaultOpen);
function enter(el) { function enter(el) {
const elementHeight = el.getBoundingClientRect().height; const elementHeight = el.getBoundingClientRect().height;
@ -98,20 +98,20 @@ function afterLeave(el) {
} }
function toggle() { function toggle() {
if (!opened) { if (!opened.value) {
openedAtLeastOnce = true; openedAtLeastOnce.value = true;
} }
nextTick(() => { nextTick(() => {
opened = !opened; opened.value = !opened.value;
}); });
} }
onMounted(() => { onMounted(() => {
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(document.documentElement);
const parentBg = getBgColor(rootEl.parentElement); const parentBg = getBgColor(rootEl.value.parentElement);
const myBg = computedStyle.getPropertyValue('--panel'); const myBg = computedStyle.getPropertyValue('--panel');
bgSame = parentBg === myBg; bgSame.value = parentBg === myBg;
}); });
</script> </script>

View File

@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted } from 'vue'; import { onBeforeUnmount, onMounted, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { useStream } from '@/stream.js'; import { useStream } from '@/stream.js';
@ -57,9 +57,9 @@ const emit = defineEmits<{
(_: 'update:user', value: Misskey.entities.UserDetailed): void (_: 'update:user', value: Misskey.entities.UserDetailed): void
}>(); }>();
let isFollowing = $ref(props.user.isFollowing); const isFollowing = ref(props.user.isFollowing);
let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou); const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou);
let wait = $ref(false); const wait = ref(false);
const connection = useStream().useChannel('main'); const connection = useStream().useChannel('main');
if (props.user.isFollowing == null) { if (props.user.isFollowing == null) {
@ -71,16 +71,16 @@ if (props.user.isFollowing == null) {
function onFollowChange(user: Misskey.entities.UserDetailed) { function onFollowChange(user: Misskey.entities.UserDetailed) {
if (user.id === props.user.id) { if (user.id === props.user.id) {
isFollowing = user.isFollowing; isFollowing.value = user.isFollowing;
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou; hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou;
} }
} }
async function onClick() { async function onClick() {
wait = true; wait.value = true;
try { try {
if (isFollowing) { if (isFollowing.value) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }), text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
@ -92,11 +92,11 @@ async function onClick() {
userId: props.user.id, userId: props.user.id,
}); });
} else { } else {
if (hasPendingFollowRequestFromYou) { if (hasPendingFollowRequestFromYou.value) {
await os.api('following/requests/cancel', { await os.api('following/requests/cancel', {
userId: props.user.id, userId: props.user.id,
}); });
hasPendingFollowRequestFromYou = false; hasPendingFollowRequestFromYou.value = false;
} else { } else {
await os.api('following/create', { await os.api('following/create', {
userId: props.user.id, userId: props.user.id,
@ -106,7 +106,7 @@ async function onClick() {
...props.user, ...props.user,
withReplies: defaultStore.state.defaultWithReplies withReplies: defaultStore.state.defaultWithReplies
}); });
hasPendingFollowRequestFromYou = true; hasPendingFollowRequestFromYou.value = true;
claimAchievement('following1'); claimAchievement('following1');
@ -127,7 +127,7 @@ async function onClick() {
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} finally { } finally {
wait = false; wait.value = false;
} }
} }

View File

@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
@ -53,19 +53,19 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let dialog: InstanceType<typeof MkModalWindow> = $ref(); const dialog = ref<InstanceType<typeof MkModalWindow>>();
let username = $ref(''); const username = ref('');
let email = $ref(''); const email = ref('');
let processing = $ref(false); const processing = ref(false);
async function onSubmit() { async function onSubmit() {
processing = true; processing.value = true;
await os.apiWithDialog('request-reset-password', { await os.apiWithDialog('request-reset-password', {
username, username: username.value,
email, email: email.value,
}); });
emit('done'); emit('done');
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, nextTick, watch } from 'vue'; import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -27,11 +27,11 @@ const props = defineProps<{
src: string; src: string;
}>(); }>();
const rootEl = $shallowRef<HTMLDivElement>(null); const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = $shallowRef<HTMLCanvasElement>(null); const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date(); const now = new Date();
let chartInstance: Chart = null; let chartInstance: Chart = null;
let fetching = $ref(true); const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip({ const { handler: externalTooltipHandler } = useChartTooltip({
position: 'middle', position: 'middle',
@ -42,8 +42,8 @@ async function renderChart() {
chartInstance.destroy(); chartInstance.destroy();
} }
const wide = rootEl.offsetWidth > 700; const wide = rootEl.value.offsetWidth > 700;
const narrow = rootEl.offsetWidth < 400; const narrow = rootEl.value.offsetWidth < 400;
const weeks = wide ? 50 : narrow ? 10 : 25; const weeks = wide ? 50 : narrow ? 10 : 25;
const chartLimit = 7 * weeks; const chartLimit = 7 * weeks;
@ -88,7 +88,7 @@ async function renderChart() {
values = raw.deliverFailed; values = raw.deliverFailed;
} }
fetching = false; fetching.value = false;
await nextTick(); await nextTick();
@ -101,7 +101,7 @@ async function renderChart() {
const marginEachCell = 4; const marginEachCell = 4;
chartInstance = new Chart(chartEl, { chartInstance = new Chart(chartEl.value, {
type: 'matrix', type: 'matrix',
data: { data: {
datasets: [{ datasets: [{
@ -210,7 +210,7 @@ async function renderChart() {
} }
watch(() => props.src, () => { watch(() => props.src, () => {
fetching = true; fetching.value = true;
renderChart(); renderChart();
}); });

View File

@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts"> <script lang="ts">
import { $ref } from 'vue/macros';
import DrawBlurhash from '@/workers/draw-blurhash?worker'; import DrawBlurhash from '@/workers/draw-blurhash?worker';
import TestWebGL2 from '@/workers/test-webgl2?worker'; import TestWebGL2 from '@/workers/test-webgl2?worker';
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js'; import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js';
@ -58,7 +57,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
</script> </script>
<script lang="ts" setup> <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 { v4 as uuid } from 'uuid';
import { render } from 'buraha'; import { render } from 'buraha';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -98,41 +97,41 @@ const viewId = uuid();
const canvas = shallowRef<HTMLCanvasElement>(); const canvas = shallowRef<HTMLCanvasElement>();
const root = shallowRef<HTMLDivElement>(); const root = shallowRef<HTMLDivElement>();
const img = shallowRef<HTMLImageElement>(); const img = shallowRef<HTMLImageElement>();
let loaded = $ref(false); const loaded = ref(false);
let canvasWidth = $ref(64); const canvasWidth = ref(64);
let canvasHeight = $ref(64); const canvasHeight = ref(64);
let imgWidth = $ref(props.width); const imgWidth = ref(props.width);
let imgHeight = $ref(props.height); const imgHeight = ref(props.height);
let bitmapTmp = $ref<CanvasImageSource | undefined>(); const bitmapTmp = ref<CanvasImageSource | undefined>();
const hide = computed(() => !loaded || props.forceBlurhash); const hide = computed(() => !loaded.value || props.forceBlurhash);
function waitForDecode() { function waitForDecode() {
if (props.src != null && props.src !== '') { if (props.src != null && props.src !== '') {
nextTick() nextTick()
.then(() => img.value?.decode()) .then(() => img.value?.decode())
.then(() => { .then(() => {
loaded = true; loaded.value = true;
}, error => { }, error => {
console.log('Error occurred during decoding image', img.value, error); console.log('Error occurred during decoding image', img.value, error);
}); });
} else { } else {
loaded = false; loaded.value = false;
} }
} }
watch([() => props.width, () => props.height, root], () => { watch([() => props.width, () => props.height, root], () => {
const ratio = props.width / props.height; const ratio = props.width / props.height;
if (ratio > 1) { if (ratio > 1) {
canvasWidth = Math.round(64 * ratio); canvasWidth.value = Math.round(64 * ratio);
canvasHeight = 64; canvasHeight.value = 64;
} else { } else {
canvasWidth = 64; canvasWidth.value = 64;
canvasHeight = Math.round(64 / ratio); canvasHeight.value = Math.round(64 / ratio);
} }
const clientWidth = root.value?.clientWidth ?? 300; const clientWidth = root.value?.clientWidth ?? 300;
imgWidth = clientWidth; imgWidth.value = clientWidth;
imgHeight = Math.round(clientWidth / ratio); imgHeight.value = Math.round(clientWidth / ratio);
}, { }, {
immediate: true, immediate: true,
}); });
@ -140,15 +139,15 @@ watch([() => props.width, () => props.height, root], () => {
function drawImage(bitmap: CanvasImageSource) { function drawImage(bitmap: CanvasImageSource) {
// canvasmountedTmp // canvasmountedTmp
if (!canvas.value) { if (!canvas.value) {
bitmapTmp = bitmap; bitmapTmp.value = bitmap;
return; return;
} }
// canvas // canvas
bitmapTmp = undefined; bitmapTmp.value = undefined;
const ctx = canvas.value.getContext('2d'); const ctx = canvas.value.getContext('2d');
if (!ctx) return; if (!ctx) return;
ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight); ctx.drawImage(bitmap, 0, 0, canvasWidth.value, canvasHeight.value);
} }
function drawAvg() { function drawAvg() {
@ -160,7 +159,7 @@ function drawAvg() {
// avgColor // avgColor
ctx.beginPath(); ctx.beginPath();
ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888'; ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
ctx.fillRect(0, 0, canvasWidth, canvasHeight); ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value);
} }
async function draw() { async function draw() {
@ -212,8 +211,8 @@ watch(() => props.hash, () => {
onMounted(() => { onMounted(() => {
// drawImagemounted // drawImagemounted
if (bitmapTmp) { if (bitmapTmp.value) {
drawImage(bitmapTmp); drawImage(bitmapTmp.value);
} }
waitForDecode(); waitForDecode();
}); });

View File

@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkMiniChart from '@/components/MkMiniChart.vue'; import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -24,12 +25,12 @@ const props = defineProps<{
instance: Misskey.entities.FederationInstance; 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 => { os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
// //
res['requests.received'].splice(0, 1); res['requests.received'].splice(0, 1);
chartValues = res['requests.received']; chartValues.value = res['requests.received'];
}); });
function getInstanceIcon(instance): string { function getInstanceIcon(instance): string {

View File

@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref, shallowRef } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import MkChart from '@/components/MkChart.vue'; import MkChart from '@/components/MkChart.vue';
@ -100,11 +100,11 @@ import { initChart } from '@/scripts/init-chart.js';
initChart(); initChart();
const chartLimit = 500; const chartLimit = 500;
let chartSpan = $ref<'hour' | 'day'>('hour'); const chartSpan = ref<'hour' | 'day'>('hour');
let chartSrc = $ref('active-users'); const chartSrc = ref('active-users');
let heatmapSrc = $ref('active-users'); const heatmapSrc = ref('active-users');
let subDoughnutEl = $shallowRef<HTMLCanvasElement>(); const subDoughnutEl = shallowRef<HTMLCanvasElement>();
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>(); const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
const { handler: externalTooltipHandler1 } = useChartTooltip({ const { handler: externalTooltipHandler1 } = useChartTooltip({
position: 'middle', position: 'middle',
@ -163,7 +163,7 @@ function createDoughnut(chartEl, tooltip, data) {
onMounted(() => { onMounted(() => {
os.apiGet('federation/stats', { limit: 30 }).then(fedStats => { 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, name: x.host,
color: x.themeColor, color: x.themeColor,
value: x.followersCount, value: x.followersCount,
@ -172,7 +172,7 @@ onMounted(() => {
}, },
})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }])); })).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, name: x.host,
color: x.themeColor, color: x.themeColor,
value: x.followingCount, value: x.followingCount,

View File

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
import { instanceName } from '@/config.js'; import { instanceName } from '@/config.js';
import { instance as Instance } from '@/instance.js'; import { instance as Instance } from '@/instance.js';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.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, 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'; const themeColor = instance.themeColor ?? '#777777';

View File

@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { shallowRef } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import { navbarItemDef } from '@/navbar.js'; import { navbarItemDef } from '@/navbar.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -48,7 +48,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop
deviceKind === 'smartphone' ? 'drawer' : deviceKind === 'smartphone' ? 'drawer' :
'dialog'; 'dialog';
const modal = $shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
const menu = defaultStore.state.menu; const menu = defaultStore.state.menu;
@ -63,7 +63,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k =>
})); }));
function close() { function close() {
modal.close(); modal.value.close();
} }
</script> </script>

View File

@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent, ref } from 'vue';
import { url as local } from '@/config.js'; import { url as local } from '@/config.js';
import { useTooltip } from '@/scripts/use-tooltip.js'; import { useTooltip } from '@/scripts/use-tooltip.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -29,13 +29,13 @@ const self = props.url.startsWith(local);
const attr = self ? 'to' : 'href'; const attr = self ? 'to' : 'href';
const target = self ? null : '_blank'; const target = self ? null : '_blank';
const el = $ref(); const el = ref();
useTooltip($$(el), (showing) => { useTooltip(el, (showing) => {
os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), { os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
showing, showing,
url: props.url, url: props.url,
source: el, source: el.value,
}, {}, 'closed'); }, {}, 'closed');
}); });
</script> </script>

View File

@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, shallowRef, watch } from 'vue'; import { onMounted, shallowRef, watch, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -42,7 +42,7 @@ const props = withDefaults(defineProps<{
}); });
const audioEl = shallowRef<HTMLAudioElement>(); const audioEl = shallowRef<HTMLAudioElement>();
let hide = $ref(true); const hide = ref(true);
watch(audioEl, () => { watch(audioEl, () => {
if (audioEl.value) { if (audioEl.value) {

View File

@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from 'vue'; import { watch, ref, computed } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { getStaticImageUrl } from '@/scripts/media-proxy.js';
import bytes from '@/filters/bytes.js'; import bytes from '@/filters/bytes.js';
@ -73,10 +73,10 @@ const props = withDefaults(defineProps<{
controls: true, controls: true,
}); });
let hide = $ref(true); const hide = ref(true);
let darkMode: boolean = $ref(defaultStore.state.darkMode); 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 ? props.image.url
: defaultStore.state.disableShowingAnimatedImages : defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(props.image.url) ? getStaticImageUrl(props.image.url)
@ -87,14 +87,14 @@ function onclick() {
if (!props.controls) { if (!props.controls) {
return; return;
} }
if (hide) { if (hide.value) {
hide = false; hide.value = false;
} }
} }
// Plugin:register_note_view_interruptor 使watch // Plugin:register_note_view_interruptor 使watch
watch(() => props.image, () => { 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, deep: true,
immediate: true, immediate: true,
@ -105,7 +105,7 @@ function showMenu(ev: MouseEvent) {
text: i18n.ts.hide, text: i18n.ts.hide,
icon: 'ti ti-eye-off', icon: 'ti ti-eye-off',
action: () => { action: () => {
hide = true; hide.value = true;
}, },
}, ...(iAmModerator ? [{ }, ...(iAmModerator ? [{
text: i18n.ts.markAsSensitive, text: i18n.ts.markAsSensitive,

View File

@ -63,7 +63,7 @@ async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLE
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, shallowRef } from 'vue'; import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import PhotoSwipeLightbox from 'photoswipe/lightbox'; import PhotoSwipeLightbox from 'photoswipe/lightbox';
import PhotoSwipe from 'photoswipe'; import PhotoSwipe from 'photoswipe';
@ -86,7 +86,7 @@ const container = shallowRef<HTMLElement | null | undefined>(undefined);
const gallery = shallowRef<HTMLDivElement>(); const gallery = shallowRef<HTMLDivElement>();
const pswpZIndex = os.claimZIndex('middle'); const pswpZIndex = os.claimZIndex('middle');
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); 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; let lightbox: PhotoSwipeLightbox | null;
const popstateHandler = (): void => { const popstateHandler = (): void => {

View File

@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts"> <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 { focusPrev, focusNext } from '@/scripts/focus.js';
import MkSwitchButton from '@/components/MkSwitch.button.vue'; import MkSwitchButton from '@/components/MkSwitch.button.vue';
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
@ -90,19 +90,19 @@ const emit = defineEmits<{
(ev: 'hide'): void; (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, 'up|k|shift+tab': focusUp,
'down|j|tab': focusDown, 'down|j|tab': focusDown,
'esc': close, 'esc': close,
})); }));
let childShowingItem = $ref<MenuItem | null>(); const childShowingItem = ref<MenuItem | null>();
let preferClick = isTouchUsing || props.asDrawer; let preferClick = isTouchUsing || props.asDrawer;
@ -115,22 +115,22 @@ watch(() => props.items, () => {
if (item && 'then' in item) { // if item is Promise if (item && 'then' in item) { // if item is Promise
items[i] = { type: 'pending' }; items[i] = { type: 'pending' };
item.then(actualItem => { item.then(actualItem => {
items2[i] = actualItem; items2.value[i] = actualItem;
}); });
} }
} }
items2 = items as InnerMenuItem[]; items2.value = items as InnerMenuItem[];
}, { }, {
immediate: true, immediate: true,
}); });
const childMenu = ref<MenuItem[] | null>(); const childMenu = ref<MenuItem[] | null>();
let childTarget = $shallowRef<HTMLElement | null>(); const childTarget = shallowRef<HTMLElement | null>();
function closeChild() { function closeChild() {
childMenu.value = null; childMenu.value = null;
childShowingItem = null; childShowingItem.value = null;
} }
function childActioned() { function childActioned() {
@ -139,8 +139,8 @@ function childActioned() {
} }
const onGlobalMousedown = (event: MouseEvent) => { const onGlobalMousedown = (event: MouseEvent) => {
if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return; if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return;
if (child && child.checkHit(event)) return; if (child.value && child.value.checkHit(event)) return;
closeChild(); closeChild();
}; };
@ -177,10 +177,10 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
}); });
emit('hide'); emit('hide');
} else { } else {
childTarget = ev.currentTarget ?? ev.target; childTarget.value = ev.currentTarget ?? ev.target;
// //
childMenu.value = children; childMenu.value = children;
childShowingItem = item; childShowingItem.value = item;
} }
} }
@ -209,7 +209,7 @@ function switchItem(item: MenuSwitch & { ref: any }) {
onMounted(() => { onMounted(() => {
if (props.viaKeyboard) { if (props.viaKeyboard) {
nextTick(() => { nextTick(() => {
if (itemsEl) focusNext(itemsEl.children[0], true, false); if (itemsEl.value) focusNext(itemsEl.value.children[0], true, false);
}); });
} }

View File

@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from 'vue'; import { watch, ref } from 'vue';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { useInterval } from '@/scripts/use-interval.js'; import { useInterval } from '@/scripts/use-interval.js';
@ -43,11 +43,11 @@ const props = defineProps<{
const viewBoxX = 50; const viewBoxX = 50;
const viewBoxY = 50; const viewBoxY = 50;
const gradientId = uuid(); const gradientId = uuid();
let polylinePoints = $ref(''); const polylinePoints = ref('');
let polygonPoints = $ref(''); const polygonPoints = ref('');
let headX = $ref<number | null>(null); const headX = ref<number | null>(null);
let headY = $ref<number | null>(null); const headY = ref<number | null>(null);
let clock = $ref<number | null>(null); const clock = ref<number | null>(null);
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')); const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
const color = accent.toRgbString(); const color = accent.toRgbString();
@ -60,12 +60,12 @@ function draw(): void {
(1 - (n / peak)) * viewBoxY, (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]; headX.value = _polylinePoints.at(-1)![0];
headY = _polylinePoints.at(-1)![1]; headY.value = _polylinePoints.at(-1)![1];
} }
watch(() => props.src, draw, { immediate: true }); watch(() => props.src, draw, { immediate: true });

View File

@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 * as os from '@/os.js';
import { isTouchUsing } from '@/scripts/touch.js'; import { isTouchUsing } from '@/scripts/touch.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -89,14 +89,14 @@ const emit = defineEmits<{
provide('modal', true); provide('modal', true);
let maxHeight = $ref<number>(); const maxHeight = ref<number>();
let fixed = $ref(false); const fixed = ref(false);
let transformOrigin = $ref('center'); const transformOrigin = ref('center');
let showing = $ref(true); const showing = ref(true);
let content = $shallowRef<HTMLElement>(); const content = shallowRef<HTMLElement>();
const zIndex = os.claimZIndex(props.zPriority); const zIndex = os.claimZIndex(props.zPriority);
let useSendAnime = $ref(false); const useSendAnime = ref(false);
const type = $computed<ModalTypes>(() => { const type = computed<ModalTypes>(() => {
if (props.preferType === 'auto') { if (props.preferType === 'auto') {
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') { if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
return 'drawer'; return 'drawer';
@ -107,26 +107,26 @@ const type = $computed<ModalTypes>(() => {
return props.preferType!; return props.preferType!;
} }
}); });
const isEnableBgTransparent = $computed(() => props.transparentBg && (type === 'popup')); const isEnableBgTransparent = computed(() => props.transparentBg && (type.value === 'popup'));
let transitionName = $computed((() => const transitionName = computed((() =>
defaultStore.state.animation defaultStore.state.animation
? useSendAnime ? useSendAnime.value
? 'send' ? 'send'
: type === 'drawer' : type.value === 'drawer'
? 'modal-drawer' ? 'modal-drawer'
: type === 'popup' : type.value === 'popup'
? 'modal-popup' ? 'modal-popup'
: 'modal' : 'modal'
: '' : ''
)); ));
let transitionDuration = $computed((() => const transitionDuration = computed((() =>
transitionName === 'send' transitionName.value === 'send'
? 400 ? 400
: transitionName === 'modal-popup' : transitionName.value === 'modal-popup'
? 100 ? 100
: transitionName === 'modal' : transitionName.value === 'modal'
? 200 ? 200
: transitionName === 'modal-drawer' : transitionName.value === 'modal-drawer'
? 200 ? 200
: 0 : 0
)); ));
@ -135,12 +135,12 @@ let contentClicking = false;
function close(opts: { useSendAnimation?: boolean } = {}) { function close(opts: { useSendAnimation?: boolean } = {}) {
if (opts.useSendAnimation) { if (opts.useSendAnimation) {
useSendAnime = true; useSendAnime.value = true;
} }
// eslint-disable-next-line vue/no-mutating-props // eslint-disable-next-line vue/no-mutating-props
if (props.src) props.src.style.pointerEvents = 'auto'; if (props.src) props.src.style.pointerEvents = 'auto';
showing = false; showing.value = false;
emit('close'); emit('close');
} }
@ -149,8 +149,8 @@ function onBgClick() {
emit('click'); emit('click');
} }
if (type === 'drawer') { if (type.value === 'drawer') {
maxHeight = window.innerHeight / 1.5; maxHeight.value = window.innerHeight / 1.5;
} }
const keymap = { const keymap = {
@ -162,21 +162,21 @@ const SCROLLBAR_THICKNESS = 16;
const align = () => { const align = () => {
if (props.src == null) return; if (props.src == null) return;
if (type === 'drawer') return; if (type.value === 'drawer') return;
if (type === 'dialog') return; if (type.value === 'dialog') return;
if (content == null) return; if (content.value == null) return;
const srcRect = props.src.getBoundingClientRect(); const srcRect = props.src.getBoundingClientRect();
const width = content!.offsetWidth; const width = content.value!.offsetWidth;
const height = content!.offsetHeight; const height = content.value!.offsetHeight;
let left; let left;
let top; let top;
const x = srcRect.left + (fixed ? 0 : window.pageXOffset); const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
const y = srcRect.top + (fixed ? 0 : window.pageYOffset); const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
if (props.anchor.x === 'center') { if (props.anchor.x === 'center') {
left = x + (props.src.offsetWidth / 2) - (width / 2); left = x + (props.src.offsetWidth / 2) - (width / 2);
@ -194,7 +194,7 @@ const align = () => {
top = y + props.src.offsetHeight; top = y + props.src.offsetHeight;
} }
if (fixed) { if (fixed.value) {
// //
if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) { if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width; left = (window.innerWidth - SCROLLBAR_THICKNESS) - width;
@ -207,16 +207,16 @@ const align = () => {
if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
if (props.noOverlap && props.anchor.x === 'center') { if (props.noOverlap && props.anchor.x === 'center') {
if (underSpace >= (upperSpace / 3)) { if (underSpace >= (upperSpace / 3)) {
maxHeight = underSpace; maxHeight.value = underSpace;
} else { } else {
maxHeight = upperSpace; maxHeight.value = upperSpace;
top = (upperSpace + MARGIN) - height; top = (upperSpace + MARGIN) - height;
} }
} else { } else {
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height; top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height;
} }
} else { } else {
maxHeight = underSpace; maxHeight.value = underSpace;
} }
} else { } else {
// //
@ -231,16 +231,16 @@ const align = () => {
if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
if (props.noOverlap && props.anchor.x === 'center') { if (props.noOverlap && props.anchor.x === 'center') {
if (underSpace >= (upperSpace / 3)) { if (underSpace >= (upperSpace / 3)) {
maxHeight = underSpace; maxHeight.value = underSpace;
} else { } else {
maxHeight = upperSpace; maxHeight.value = upperSpace;
top = window.pageYOffset + ((upperSpace + MARGIN) - height); top = window.pageYOffset + ((upperSpace + MARGIN) - height);
} }
} else { } else {
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1; top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
} }
} else { } else {
maxHeight = underSpace; maxHeight.value = underSpace;
} }
} }
@ -255,29 +255,29 @@ const align = () => {
let transformOriginX = 'center'; let transformOriginX = 'center';
let transformOriginY = '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'; 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'; 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'; 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'; transformOriginX = 'right';
} }
transformOrigin = `${transformOriginX} ${transformOriginY}`; transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
content.style.left = left + 'px'; content.value.style.left = left + 'px';
content.style.top = top + 'px'; content.value.style.top = top + 'px';
}; };
const onOpened = () => { const onOpened = () => {
emit('opened'); emit('opened');
// //
const el = content!.children[0]; const el = content.value!.children[0];
el.addEventListener('mousedown', ev => { el.addEventListener('mousedown', ev => {
contentClicking = true; contentClicking = true;
window.addEventListener('mouseup', ev => { window.addEventListener('mouseup', ev => {
@ -299,7 +299,7 @@ onMounted(() => {
// eslint-disable-next-line vue/no-mutating-props // eslint-disable-next-line vue/no-mutating-props
props.src.style.pointerEvents = 'none'; props.src.style.pointerEvents = 'none';
} }
fixed = (type === 'drawer') || (getFixedContainer(props.src) != null); fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
await nextTick(); await nextTick();
@ -307,7 +307,7 @@ onMounted(() => {
}, { immediate: true }); }, { immediate: true });
nextTick(() => { nextTick(() => {
alignObserver.observe(content!); alignObserver.observe(content.value!);
}); });
}); });

View File

@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
import MkModal from './MkModal.vue'; import MkModal from './MkModal.vue';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -44,14 +44,14 @@ const emit = defineEmits<{
(event: 'ok'): void; (event: 'ok'): void;
}>(); }>();
let modal = $shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
let rootEl = $shallowRef<HTMLElement>(); const rootEl = shallowRef<HTMLElement>();
let headerEl = $shallowRef<HTMLElement>(); const headerEl = shallowRef<HTMLElement>();
let bodyWidth = $ref(0); const bodyWidth = ref(0);
let bodyHeight = $ref(0); const bodyHeight = ref(0);
const close = () => { const close = () => {
modal.close(); modal.value.close();
}; };
const onBgClick = () => { const onBgClick = () => {
@ -67,14 +67,14 @@ const onKeydown = (evt) => {
}; };
const ro = new ResizeObserver((entries, observer) => { const ro = new ResizeObserver((entries, observer) => {
bodyWidth = rootEl.offsetWidth; bodyWidth.value = rootEl.value.offsetWidth;
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight; bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
}); });
onMounted(() => { onMounted(() => {
bodyWidth = rootEl.offsetWidth; bodyWidth.value = rootEl.value.offsetWidth;
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight; bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
ro.observe(rootEl); ro.observe(rootEl.value);
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -205,12 +205,12 @@ const emit = defineEmits<{
const inChannel = inject('inChannel', null); const inChannel = inject('inChannel', null);
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
let note = $ref(deepClone(props.note)); const note = ref(deepClone(props.note));
// plugin // plugin
if (noteViewInterruptors.length > 0) { if (noteViewInterruptors.length > 0) {
onMounted(async () => { onMounted(async () => {
let result: Misskey.entities.Note | null = deepClone(note); let result: Misskey.entities.Note | null = deepClone(note.value);
for (const interruptor of noteViewInterruptors) { for (const interruptor of noteViewInterruptors) {
try { try {
result = await interruptor.handler(result); result = await interruptor.handler(result);
@ -222,15 +222,15 @@ if (noteViewInterruptors.length > 0) {
console.error(err); console.error(err);
} }
} }
note = result; note.value = result;
}); });
} }
const isRenote = ( const isRenote = (
note.renote != null && note.value.renote != null &&
note.text == null && note.value.text == null &&
note.fileIds.length === 0 && note.value.fileIds.length === 0 &&
note.poll == null note.value.poll == null
); );
const el = shallowRef<HTMLElement>(); const el = shallowRef<HTMLElement>();
@ -239,21 +239,21 @@ const renoteButton = shallowRef<HTMLElement>();
const renoteTime = shallowRef<HTMLElement>(); const renoteTime = shallowRef<HTMLElement>();
const reactButton = shallowRef<HTMLElement>(); const reactButton = shallowRef<HTMLElement>();
const clipButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>();
let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note); const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
const isMyRenote = $i && ($i.id === note.userId); const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false); const showContent = ref(false);
const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null); const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
const urls = $computed(() => parsed ? extractUrlFromMfm(parsed) : null); const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value) : null);
const isLong = shouldCollapsed(appearNote, urls ?? []); const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
const collapsed = ref(appearNote.cw == null && isLong); const collapsed = ref(appearNote.value.cw == null && isLong);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref(checkMute(appearNote, $i?.mutedWords)); const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
const hardMuted = ref(props.withHardMute && checkMute(appearNote, $i?.hardMutedWords)); const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords));
const translation = ref<any>(null); const translation = ref<any>(null);
const translating = ref(false); const translating = ref(false);
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 canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id)); const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id));
let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null))); 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 { function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean {
if (mutedWords == null) return false; if (mutedWords == null) return false;
@ -277,20 +277,20 @@ const keymap = {
provide('react', (reaction: string) => { provide('react', (reaction: string) => {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: reaction, reaction: reaction,
}); });
}); });
if (props.mock) { if (props.mock) {
watch(() => props.note, (to) => { watch(() => props.note, (to) => {
note = deepClone(to); note.value = deepClone(to);
}, { deep: true }); }, { deep: true });
} else { } else {
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(appearNote), note: appearNote,
pureNote: $$(note), pureNote: note,
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
} }
@ -298,7 +298,7 @@ if (props.mock) {
if (!props.mock) { if (!props.mock) {
useTooltip(renoteButton, async (showing) => { useTooltip(renoteButton, async (showing) => {
const renotes = await os.api('notes/renotes', { const renotes = await os.api('notes/renotes', {
noteId: appearNote.id, noteId: appearNote.value.id,
limit: 11, limit: 11,
}); });
@ -309,7 +309,7 @@ if (!props.mock) {
os.popup(MkUsersTooltip, { os.popup(MkUsersTooltip, {
showing, showing,
users, users,
count: appearNote.renoteCount, count: appearNote.value.renoteCount,
targetElement: renoteButton.value, targetElement: renoteButton.value,
}, {}, 'closed'); }, {}, 'closed');
}); });
@ -319,7 +319,7 @@ function renote(viaKeyboard = false) {
pleaseLogin(); pleaseLogin();
showMovedDialog(); 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, { os.popupMenu(menu, renoteButton.value, {
viaKeyboard, viaKeyboard,
}); });
@ -331,8 +331,8 @@ function reply(viaKeyboard = false): void {
return; return;
} }
os.post({ os.post({
reply: appearNote, reply: appearNote.value,
channel: appearNote.channel, channel: appearNote.value.channel,
animation: !viaKeyboard, animation: !viaKeyboard,
}, () => { }, () => {
focus(); focus();
@ -342,7 +342,7 @@ function reply(viaKeyboard = false): void {
function react(viaKeyboard = false): void { function react(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
showMovedDialog(); showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') { if (appearNote.value.reactionAcceptance === 'likeOnly') {
sound.play('reaction'); sound.play('reaction');
if (props.mock) { if (props.mock) {
@ -350,7 +350,7 @@ function react(viaKeyboard = false): void {
} }
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: '❤️', reaction: '❤️',
}); });
const el = reactButton.value as HTMLElement | null | undefined; const el = reactButton.value as HTMLElement | null | undefined;
@ -371,10 +371,10 @@ function react(viaKeyboard = false): void {
} }
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: reaction, 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'); claimAchievement('reactWithoutRead');
} }
}, () => { }, () => {
@ -417,7 +417,7 @@ function onContextmenu(ev: MouseEvent): void {
ev.preventDefault(); ev.preventDefault();
react(); react();
} else { } 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); os.contextMenu(menu, ev).then(focus).finally(cleanup);
} }
} }
@ -427,7 +427,7 @@ function menu(viaKeyboard = false): void {
return; 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, { os.popupMenu(menu, menuButton.value, {
viaKeyboard, viaKeyboard,
}).then(focus).finally(cleanup); }).then(focus).finally(cleanup);
@ -438,7 +438,7 @@ async function clip() {
return; 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 { function showRenoteMenu(viaKeyboard = false): void {
@ -453,7 +453,7 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true, danger: true,
action: () => { action: () => {
os.api('notes/delete', { os.api('notes/delete', {
noteId: note.id, noteId: note.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
}, },
@ -463,7 +463,7 @@ function showRenoteMenu(viaKeyboard = false): void {
if (isMyRenote) { if (isMyRenote) {
pleaseLogin(); pleaseLogin();
os.popupMenu([ os.popupMenu([
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote), getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
null, null,
getUnrenote(), getUnrenote(),
], renoteTime.value, { ], renoteTime.value, {
@ -471,9 +471,9 @@ function showRenoteMenu(viaKeyboard = false): void {
}); });
} else { } else {
os.popupMenu([ os.popupMenu([
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote), getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
null, null,
getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote), getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote),
$i.isModerator || $i.isAdmin ? getUnrenote() : undefined, $i.isModerator || $i.isAdmin ? getUnrenote() : undefined,
], renoteTime.value, { ], renoteTime.value, {
viaKeyboard: viaKeyboard, viaKeyboard: viaKeyboard,
@ -499,7 +499,7 @@ function focusAfter() {
function readPromo() { function readPromo() {
os.api('promo/read', { os.api('promo/read', {
noteId: appearNote.id, noteId: appearNote.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
} }

View File

@ -235,12 +235,12 @@ const props = defineProps<{
const inChannel = inject('inChannel', null); const inChannel = inject('inChannel', null);
let note = $ref(deepClone(props.note)); const note = ref(deepClone(props.note));
// plugin // plugin
if (noteViewInterruptors.length > 0) { if (noteViewInterruptors.length > 0) {
onMounted(async () => { onMounted(async () => {
let result: Misskey.entities.Note | null = deepClone(note); let result: Misskey.entities.Note | null = deepClone(note.value);
for (const interruptor of noteViewInterruptors) { for (const interruptor of noteViewInterruptors) {
try { try {
result = await interruptor.handler(result); result = await interruptor.handler(result);
@ -252,15 +252,15 @@ if (noteViewInterruptors.length > 0) {
console.error(err); console.error(err);
} }
} }
note = result; note.value = result;
}); });
} }
const isRenote = ( const isRenote = (
note.renote != null && note.value.renote != null &&
note.text == null && note.value.text == null &&
note.fileIds.length === 0 && note.value.fileIds.length === 0 &&
note.poll == null note.value.poll == null
); );
const el = shallowRef<HTMLElement>(); const el = shallowRef<HTMLElement>();
@ -269,19 +269,19 @@ const renoteButton = shallowRef<HTMLElement>();
const renoteTime = shallowRef<HTMLElement>(); const renoteTime = shallowRef<HTMLElement>();
const reactButton = shallowRef<HTMLElement>(); const reactButton = shallowRef<HTMLElement>();
const clipButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>();
let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note); const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
const isMyRenote = $i && ($i.id === note.userId); const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false); const showContent = ref(false);
const isDeleted = 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 translation = ref(null);
const translating = ref(false); 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 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 conversation = ref<Misskey.entities.Note[]>([]);
const replies = 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 = { const keymap = {
'r': () => reply(true), 'r': () => reply(true),
@ -294,41 +294,41 @@ const keymap = {
provide('react', (reaction: string) => { provide('react', (reaction: string) => {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: reaction, reaction: reaction,
}); });
}); });
let tab = $ref('replies'); const tab = ref('replies');
let reactionTabType = $ref(null); const reactionTabType = ref(null);
const renotesPagination = $computed(() => ({ const renotesPagination = computed(() => ({
endpoint: 'notes/renotes', endpoint: 'notes/renotes',
limit: 10, limit: 10,
params: { params: {
noteId: appearNote.id, noteId: appearNote.value.id,
}, },
})); }));
const reactionsPagination = $computed(() => ({ const reactionsPagination = computed(() => ({
endpoint: 'notes/reactions', endpoint: 'notes/reactions',
limit: 10, limit: 10,
params: { params: {
noteId: appearNote.id, noteId: appearNote.value.id,
type: reactionTabType, type: reactionTabType.value,
}, },
})); }));
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(appearNote), note: appearNote,
pureNote: $$(note), pureNote: note,
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
useTooltip(renoteButton, async (showing) => { useTooltip(renoteButton, async (showing) => {
const renotes = await os.api('notes/renotes', { const renotes = await os.api('notes/renotes', {
noteId: appearNote.id, noteId: appearNote.value.id,
limit: 11, limit: 11,
}); });
@ -339,7 +339,7 @@ useTooltip(renoteButton, async (showing) => {
os.popup(MkUsersTooltip, { os.popup(MkUsersTooltip, {
showing, showing,
users, users,
count: appearNote.renoteCount, count: appearNote.value.renoteCount,
targetElement: renoteButton.value, targetElement: renoteButton.value,
}, {}, 'closed'); }, {}, 'closed');
}); });
@ -348,7 +348,7 @@ function renote(viaKeyboard = false) {
pleaseLogin(); pleaseLogin();
showMovedDialog(); showMovedDialog();
const { menu } = getRenoteMenu({ note: note, renoteButton }); const { menu } = getRenoteMenu({ note: note.value, renoteButton });
os.popupMenu(menu, renoteButton.value, { os.popupMenu(menu, renoteButton.value, {
viaKeyboard, viaKeyboard,
}); });
@ -358,8 +358,8 @@ function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
showMovedDialog(); showMovedDialog();
os.post({ os.post({
reply: appearNote, reply: appearNote.value,
channel: appearNote.channel, channel: appearNote.value.channel,
animation: !viaKeyboard, animation: !viaKeyboard,
}, () => { }, () => {
focus(); focus();
@ -369,11 +369,11 @@ function reply(viaKeyboard = false): void {
function react(viaKeyboard = false): void { function react(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
showMovedDialog(); showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') { if (appearNote.value.reactionAcceptance === 'likeOnly') {
sound.play('reaction'); sound.play('reaction');
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: '❤️', reaction: '❤️',
}); });
const el = reactButton.value as HTMLElement | null | undefined; const el = reactButton.value as HTMLElement | null | undefined;
@ -389,10 +389,10 @@ function react(viaKeyboard = false): void {
sound.play('reaction'); sound.play('reaction');
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: reaction, 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'); claimAchievement('reactWithoutRead');
} }
}, () => { }, () => {
@ -423,20 +423,20 @@ function onContextmenu(ev: MouseEvent): void {
ev.preventDefault(); ev.preventDefault();
react(); react();
} else { } 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); os.contextMenu(menu, ev).then(focus).finally(cleanup);
} }
} }
function menu(viaKeyboard = false): void { 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, { os.popupMenu(menu, menuButton.value, {
viaKeyboard, viaKeyboard,
}).then(focus).finally(cleanup); }).then(focus).finally(cleanup);
} }
async function clip() { 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 { function showRenoteMenu(viaKeyboard = false): void {
@ -448,7 +448,7 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true, danger: true,
action: () => { action: () => {
os.api('notes/delete', { os.api('notes/delete', {
noteId: note.id, noteId: note.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
}, },
@ -470,7 +470,7 @@ const repliesLoaded = ref(false);
function loadReplies() { function loadReplies() {
repliesLoaded.value = true; repliesLoaded.value = true;
os.api('notes/children', { os.api('notes/children', {
noteId: appearNote.id, noteId: appearNote.value.id,
limit: 30, limit: 30,
}).then(res => { }).then(res => {
replies.value = res; replies.value = res;
@ -482,7 +482,7 @@ const conversationLoaded = ref(false);
function loadConversation() { function loadConversation() {
conversationLoaded.value = true; conversationLoaded.value = true;
os.api('notes/conversation', { os.api('notes/conversation', {
noteId: appearNote.replyId, noteId: appearNote.value.replyId,
}).then(res => { }).then(res => {
conversation.value = res.reverse(); conversation.value = res.reverse();
}); });

View File

@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkNoteHeader from '@/components/MkNoteHeader.vue'; import MkNoteHeader from '@/components/MkNoteHeader.vue';
import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
@ -33,7 +33,7 @@ const props = defineProps<{
note: Misskey.entities.Note; note: Misskey.entities.Note;
}>(); }>();
const showContent = $ref(false); const showContent = ref(false);
</script> </script>
<style lang="scss" module> <style lang="scss" module>

View File

@ -65,15 +65,15 @@ const props = withDefaults(defineProps<{
const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false); const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
let showContent = $ref(false); const showContent = ref(false);
let replies: Misskey.entities.Note[] = $ref([]); const replies = ref<Misskey.entities.Note[]>([]);
if (props.detail) { if (props.detail) {
os.api('notes/children', { os.api('notes/children', {
noteId: props.note.id, noteId: props.note.id,
limit: 5, limit: 5,
}).then(res => { }).then(res => {
replies = res; replies.value = res;
}); });
} }
</script> </script>

View File

@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, Ref } from 'vue'; import { ref, Ref, shallowRef } from 'vue';
import MkSwitch from './MkSwitch.vue'; import MkSwitch from './MkSwitch.vue';
import MkInfo from './MkInfo.vue'; import MkInfo from './MkInfo.vue';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
@ -51,7 +51,7 @@ const props = withDefaults(defineProps<{
excludeTypes: () => [], 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); 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), .filter(type => !typesMap[type].value),
}); });
if (dialog) dialog.close(); if (dialog.value) dialog.value.close();
} }
function disableAll() { function disableAll() {

View File

@ -43,7 +43,7 @@ const props = defineProps<{
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); 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, endpoint: 'i/notifications-grouped' as const,
limit: 20, limit: 20,
params: computed(() => ({ params: computed(() => ({

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -22,13 +22,13 @@ const props = withDefaults(defineProps<{
maxHeight: 200, maxHeight: 200,
}); });
let content = $shallowRef<HTMLElement>(); const content = shallowRef<HTMLElement>();
let omitted = $ref(false); const omitted = ref(false);
let ignoreOmit = $ref(false); const ignoreOmit = ref(false);
const calcOmit = () => { const calcOmit = () => {
if (omitted || ignoreOmit) return; if (omitted.value || ignoreOmit.value) return;
omitted = content.offsetHeight > props.maxHeight; omitted.value = content.value.offsetHeight > props.maxHeight;
}; };
const omitObserver = new ResizeObserver((entries, observer) => { const omitObserver = new ResizeObserver((entries, observer) => {
@ -37,7 +37,7 @@ const omitObserver = new ResizeObserver((entries, observer) => {
onMounted(() => { onMounted(() => {
calcOmit(); calcOmit();
omitObserver.observe(content); omitObserver.observe(content.value);
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 RouterView from '@/components/global/RouterView.vue';
import MkWindow from '@/components/MkWindow.vue'; import MkWindow from '@/components/MkWindow.vue';
import { popout as _popout } from '@/scripts/popout.js'; 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 router = new Router(routes, props.initialPath, !!$i, page(() => import('@/pages/not-found.vue')));
const contents = shallowRef<HTMLElement>(); const contents = shallowRef<HTMLElement>();
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let windowEl = $shallowRef<InstanceType<typeof MkWindow>>(); const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
const history = $ref<{ path: string; key: any; }[]>([{ const history = ref<{ path: string; key: any; }[]>([{
path: router.getCurrentPath(), path: router.getCurrentPath(),
key: router.getCurrentKey(), key: router.getCurrentKey(),
}]); }]);
const buttonsLeft = $computed(() => { const buttonsLeft = computed(() => {
const buttons = []; const buttons = [];
if (history.length > 1) { if (history.value.length > 1) {
buttons.push({ buttons.push({
icon: 'ti ti-arrow-left', icon: 'ti ti-arrow-left',
onClick: back, onClick: back,
@ -73,7 +73,7 @@ const buttonsLeft = $computed(() => {
return buttons; return buttons;
}); });
const buttonsRight = $computed(() => { const buttonsRight = computed(() => {
const buttons = [{ const buttons = [{
icon: 'ti ti-reload', icon: 'ti ti-reload',
title: i18n.ts.reload, title: i18n.ts.reload,
@ -86,21 +86,21 @@ const buttonsRight = $computed(() => {
return buttons; return buttons;
}); });
let reloadCount = $ref(0); const reloadCount = ref(0);
router.addListener('push', ctx => { router.addListener('push', ctx => {
history.push({ path: ctx.path, key: ctx.key }); history.value.push({ path: ctx.path, key: ctx.key });
}); });
provide('router', router); provide('router', router);
provideMetadataReceiver((info) => { provideMetadataReceiver((info) => {
pageMetadata = info; pageMetadata.value = info;
}); });
provide('shouldOmitHeaderTitle', true); provide('shouldOmitHeaderTitle', true);
provide('shouldHeaderThin', true); provide('shouldHeaderThin', true);
provide('forceSpacerMin', true); provide('forceSpacerMin', true);
const contextmenu = $computed(() => ([{ const contextmenu = computed(() => ([{
icon: 'ti ti-player-eject', icon: 'ti ti-player-eject',
text: i18n.ts.showInPage, text: i18n.ts.showInPage,
action: expand, action: expand,
@ -113,7 +113,7 @@ const contextmenu = $computed(() => ([{
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
action: () => { action: () => {
window.open(url + router.getCurrentPath(), '_blank'); window.open(url + router.getCurrentPath(), '_blank');
windowEl.close(); windowEl.value.close();
}, },
}, { }, {
icon: 'ti ti-link', icon: 'ti ti-link',
@ -124,26 +124,26 @@ const contextmenu = $computed(() => ([{
}])); }]));
function back() { function back() {
history.pop(); history.value.pop();
router.replace(history.at(-1)!.path, history.at(-1)!.key); router.replace(history.value.at(-1)!.path, history.value.at(-1)!.key);
} }
function reload() { function reload() {
reloadCount++; reloadCount.value++;
} }
function close() { function close() {
windowEl.close(); windowEl.value.close();
} }
function expand() { function expand() {
mainRouter.push(router.getCurrentPath(), 'forcePage'); mainRouter.push(router.getCurrentPath(), 'forcePage');
windowEl.close(); windowEl.value.close();
} }
function popout() { function popout() {
_popout(router.getCurrentPath(), windowEl.$el); _popout(router.getCurrentPath(), windowEl.value.$el);
windowEl.close(); windowEl.value.close();
} }
useScrollPositionManager(() => getScrollContainer(contents.value), router); useScrollPositionManager(() => getScrollContainer(contents.value), router);

View File

@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts"> <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 Misskey from 'misskey-js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.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; (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, enableInfiniteScroll,
} = defaultStore.reactiveState; } = defaultStore.reactiveState;
const contentEl = $computed(() => props.pagination.pageEl ?? rootEl); const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value);
const scrollableElement = $computed(() => contentEl ? getScrollContainer(contentEl) : document.body); const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : document.body);
const visibility = useDocumentVisibility(); const visibility = useDocumentVisibility();
@ -153,35 +153,35 @@ const BACKGROUND_PAUSE_WAIT_SEC = 10;
// //
// https://qiita.com/mkataigi/items/0154aefd2223ce23398e // https://qiita.com/mkataigi/items/0154aefd2223ce23398e
let scrollObserver = $ref<IntersectionObserver>(); const scrollObserver = ref<IntersectionObserver>();
watch([() => props.pagination.reversed, $$(scrollableElement)], () => { watch([() => props.pagination.reversed, scrollableElement], () => {
if (scrollObserver) scrollObserver.disconnect(); if (scrollObserver.value) scrollObserver.value.disconnect();
scrollObserver = new IntersectionObserver(entries => { scrollObserver.value = new IntersectionObserver(entries => {
backed = entries[0].isIntersecting; backed.value = entries[0].isIntersecting;
}, { }, {
root: scrollableElement, root: scrollableElement.value,
rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px', rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px',
threshold: 0.01, threshold: 0.01,
}); });
}, { immediate: true }); }, { immediate: true });
watch($$(rootEl), () => { watch(rootEl, () => {
scrollObserver?.disconnect(); scrollObserver.value?.disconnect();
nextTick(() => { nextTick(() => {
if (rootEl) scrollObserver?.observe(rootEl); if (rootEl.value) scrollObserver.value?.observe(rootEl.value);
}); });
}); });
watch([$$(backed), $$(contentEl)], () => { watch([backed, contentEl], () => {
if (!backed) { if (!backed.value) {
if (!contentEl) return; 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 { } else {
if (scrollRemove) scrollRemove(); if (scrollRemove.value) scrollRemove.value();
scrollRemove = null; scrollRemove.value = null;
} }
}); });
@ -254,14 +254,14 @@ const fetchMore = async (): Promise<void> => {
} }
const reverseConcat = _res => { const reverseConcat = _res => {
const oldHeight = scrollableElement ? scrollableElement.scrollHeight : getBodyScrollHeight(); const oldHeight = scrollableElement.value ? scrollableElement.value.scrollHeight : getBodyScrollHeight();
const oldScroll = scrollableElement ? scrollableElement.scrollTop : window.scrollY; const oldScroll = scrollableElement.value ? scrollableElement.value.scrollTop : window.scrollY;
items.value = concatMapWithArray(items.value, _res); items.value = concatMapWithArray(items.value, _res);
return nextTick(() => { return nextTick(() => {
if (scrollableElement) { if (scrollableElement.value) {
scroll(scrollableElement, { top: oldScroll + (scrollableElement.scrollHeight - oldHeight), behavior: 'instant' }); scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' });
} else { } else {
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' }); window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
} }
@ -351,7 +351,7 @@ const appearFetchMoreAhead = async (): Promise<void> => {
fetchMoreAppearTimeout(); 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, () => { watch(visibility, () => {
if (visibility.value === 'hidden') { if (visibility.value === 'hidden') {
@ -445,11 +445,11 @@ onActivated(() => {
}); });
onDeactivated(() => { 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() { function toBottom() {
scrollToBottom(contentEl!); scrollToBottom(contentEl.value!);
} }
onBeforeMount(() => { onBeforeMount(() => {
@ -477,13 +477,13 @@ onBeforeUnmount(() => {
clearTimeout(preventAppearFetchMoreTimer.value); clearTimeout(preventAppearFetchMoreTimer.value);
preventAppearFetchMoreTimer.value = null; preventAppearFetchMoreTimer.value = null;
} }
scrollObserver?.disconnect(); scrollObserver.value?.disconnect();
}); });
defineExpose({ defineExpose({
items, items,
queue, queue,
backed, backed: backed.value,
more, more,
reload, reload,
prepend, prepend,

View File

@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, shallowRef, ref } from 'vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
@ -49,22 +49,22 @@ const emit = defineEmits<{
(ev: 'cancelled'): void; (ev: 'cancelled'): void;
}>(); }>();
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
const passwordInput = $shallowRef<InstanceType<typeof MkInput>>(); const passwordInput = shallowRef<InstanceType<typeof MkInput>>();
const password = $ref(''); const password = ref('');
const token = $ref(null); const token = ref(null);
function onClose() { function onClose() {
emit('cancelled'); emit('cancelled');
if (dialog) dialog.close(); if (dialog.value) dialog.value.close();
} }
function done(res) { function done(res) {
emit('done', { password, token }); emit('done', { password: password.value, token: token.value });
if (dialog) dialog.close(); if (dialog.value) dialog.value.close();
} }
onMounted(() => { onMounted(() => {
if (passwordInput) passwordInput.focus(); if (passwordInput.value) passwordInput.value.focus();
}); });
</script> </script>

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -23,13 +23,13 @@ const emit = defineEmits<{
(ev: 'end'): void; (ev: 'end'): void;
}>(); }>();
let up = $ref(false); const up = ref(false);
const zIndex = os.claimZIndex('middle'); const zIndex = os.claimZIndex('middle');
const angle = (45 - (Math.random() * 90)) + 'deg'; const angle = (45 - (Math.random() * 90)) + 'deg';
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {
up = true; up.value = true;
}, 10); }, 10);
window.setTimeout(() => { window.setTimeout(() => {

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref, shallowRef } from 'vue';
import MkModal from './MkModal.vue'; import MkModal from './MkModal.vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
@ -28,7 +28,7 @@ const emit = defineEmits<{
(ev: 'closing'): void; (ev: 'closing'): void;
}>(); }>();
let modal = $shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
const manualShowing = ref(true); const manualShowing = ref(true);
const hiding = ref(false); const hiding = ref(false);
@ -60,14 +60,14 @@ function hide() {
hiding.value = true; hiding.value = true;
// close // close
modal?.close(); modal.value?.close();
} }
function close() { function close() {
manualShowing.value = false; manualShowing.value = false;
// close // close
modal?.close(); modal.value?.close();
} }
</script> </script>

View File

@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 mfm from 'mfm-js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
@ -162,42 +162,42 @@ const emit = defineEmits<{
(ev: 'fileChangeSensitive', fileId: string, to: boolean): void; (ev: 'fileChangeSensitive', fileId: string, to: boolean): void;
}>(); }>();
const textareaEl = $shallowRef<HTMLTextAreaElement | null>(null); const textareaEl = shallowRef<HTMLTextAreaElement | null>(null);
const cwInputEl = $shallowRef<HTMLInputElement | null>(null); const cwInputEl = shallowRef<HTMLInputElement | null>(null);
const hashtagsInputEl = $shallowRef<HTMLInputElement | null>(null); const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null);
const visibilityButton = $shallowRef<HTMLElement | null>(null); const visibilityButton = shallowRef<HTMLElement | null>(null);
let posting = $ref(false); const posting = ref(false);
let posted = $ref(false); const posted = ref(false);
let text = $ref(props.initialText ?? ''); const text = ref(props.initialText ?? '');
let files = $ref(props.initialFiles ?? []); const files = ref(props.initialFiles ?? []);
let poll = $ref<{ const poll = ref<{
choices: string[]; choices: string[];
multiple: boolean; multiple: boolean;
expiresAt: string | null; expiresAt: string | null;
expiredAfter: string | null; expiredAfter: string | null;
} | null>(null); } | null>(null);
let useCw = $ref(false); const useCw = ref(false);
let showPreview = $ref(defaultStore.state.showPreview); const showPreview = ref(defaultStore.state.showPreview);
watch($$(showPreview), () => defaultStore.set('showPreview', showPreview)); watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
let cw = $ref<string | null>(null); const cw = ref<string | null>(null);
let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly); const 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]); const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
let visibleUsers = $ref([]); const visibleUsers = ref([]);
if (props.initialVisibleUsers) { if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser); props.initialVisibleUsers.forEach(pushVisibleUser);
} }
let reactionAcceptance = $ref(defaultStore.state.reactionAcceptance); const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
let autocomplete = $ref(null); const autocomplete = ref(null);
let draghover = $ref(false); const draghover = ref(false);
let quoteId = $ref(null); const quoteId = ref(null);
let hasNotSpecifiedMentions = $ref(false); const hasNotSpecifiedMentions = ref(false);
let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]')); const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
let imeText = $ref(''); const imeText = ref('');
let showingOptions = $ref(false); const showingOptions = ref(false);
const textAreaReadOnly = ref(false); const textAreaReadOnly = ref(false);
const draftKey = $computed((): string => { const draftKey = computed((): string => {
let key = props.channel ? `channel:${props.channel.id}` : ''; let key = props.channel ? `channel:${props.channel.id}` : '';
if (props.renote) { if (props.renote) {
@ -211,7 +211,7 @@ const draftKey = $computed((): string => {
return key; return key;
}); });
const placeholder = $computed((): string => { const placeholder = computed((): string => {
if (props.renote) { if (props.renote) {
return i18n.ts._postForm.quotePlaceholder; return i18n.ts._postForm.quotePlaceholder;
} else if (props.reply) { } else if (props.reply) {
@ -231,7 +231,7 @@ const placeholder = $computed((): string => {
} }
}); });
const submitText = $computed((): string => { const submitText = computed((): string => {
return props.renote return props.renote
? i18n.ts.quote ? i18n.ts.quote
: props.reply : props.reply
@ -239,45 +239,45 @@ const submitText = $computed((): string => {
: i18n.ts.note; : i18n.ts.note;
}); });
const textLength = $computed((): number => { const textLength = computed((): number => {
return (text + imeText).trim().length; return (text.value + imeText.value).trim().length;
}); });
const maxTextLength = $computed((): number => { const maxTextLength = computed((): number => {
return instance ? instance.maxNoteTextLength : 1000; return instance ? instance.maxNoteTextLength : 1000;
}); });
const canPost = $computed((): boolean => { const canPost = computed((): boolean => {
return !props.mock && !posting && !posted && return !props.mock && !posting.value && !posted.value &&
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && (1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
(textLength <= maxTextLength) && (textLength.value <= maxTextLength.value) &&
(!poll || poll.choices.length >= 2); (!poll.value || poll.value.choices.length >= 2);
}); });
const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags')); const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags')); const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
watch($$(text), () => { watch(text, () => {
checkMissingMention(); checkMissingMention();
}, { immediate: true }); }, { immediate: true });
watch($$(visibility), () => { watch(visibility, () => {
checkMissingMention(); checkMissingMention();
}, { immediate: true }); }, { immediate: true });
watch($$(visibleUsers), () => { watch(visibleUsers, () => {
checkMissingMention(); checkMissingMention();
}, { }, {
deep: true, deep: true,
}); });
if (props.mention) { if (props.mention) {
text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
text += ' '; text.value += ' ';
} }
if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) { 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) { 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 ($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') { if ($i?.isSilenced && visibility.value === 'public') {
visibility = 'home'; visibility.value = 'home';
} }
if (props.channel) { if (props.channel) {
visibility = 'public'; visibility.value = 'public';
localOnly = true; // TODO: localOnly.value = true; // TODO:
} }
// //
if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) { if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
if (props.reply.visibility === 'home' && visibility === 'followers') { if (props.reply.visibility === 'home' && visibility.value === 'followers') {
visibility = 'followers'; visibility.value = 'followers';
} else if (['home', 'followers'].includes(props.reply.visibility) && visibility === 'specified') { } else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') {
visibility = 'specified'; visibility.value = 'specified';
} else { } else {
visibility = props.reply.visibility; visibility.value = props.reply.visibility;
} }
if (visibility === 'specified') { if (visibility.value === 'specified') {
if (props.reply.visibleUserIds) { if (props.reply.visibleUserIds) {
os.api('users/show', { os.api('users/show', {
userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId), 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) { if (props.specified) {
visibility = 'specified'; visibility.value = 'specified';
pushVisibleUser(props.specified); pushVisibleUser(props.specified);
} }
// keep cw when reply // keep cw when reply
if (defaultStore.state.keepCw && props.reply && props.reply.cw) { if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
useCw = true; useCw.value = true;
cw = props.reply.cw; cw.value = props.reply.cw;
} }
function watchForDraft() { function watchForDraft() {
watch($$(text), () => saveDraft()); watch(text, () => saveDraft());
watch($$(useCw), () => saveDraft()); watch(useCw, () => saveDraft());
watch($$(cw), () => saveDraft()); watch(cw, () => saveDraft());
watch($$(poll), () => saveDraft()); watch(poll, () => saveDraft());
watch($$(files), () => saveDraft(), { deep: true }); watch(files, () => saveDraft(), { deep: true });
watch($$(visibility), () => saveDraft()); watch(visibility, () => saveDraft());
watch($$(localOnly), () => saveDraft()); watch(localOnly, () => saveDraft());
} }
function checkMissingMention() { function checkMissingMention() {
if (visibility === 'specified') { if (visibility.value === 'specified') {
const ast = mfm.parse(text); const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) { 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))) {
hasNotSpecifiedMentions = true; hasNotSpecifiedMentions.value = true;
return; return;
} }
} }
} }
hasNotSpecifiedMentions = false; hasNotSpecifiedMentions.value = false;
} }
function addMissingMention() { function addMissingMention() {
const ast = mfm.parse(text); const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) { 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 => { os.api('users/show', { username: x.username, host: x.host }).then(user => {
visibleUsers.push(user); visibleUsers.value.push(user);
}); });
} }
} }
} }
function togglePoll() { function togglePoll() {
if (poll) { if (poll.value) {
poll = null; poll.value = null;
} else { } else {
poll = { poll.value = {
choices: ['', ''], choices: ['', ''],
multiple: false, multiple: false,
expiresAt: null, expiresAt: null,
@ -398,13 +398,13 @@ function togglePoll() {
} }
function addTag(tag: string) { function addTag(tag: string) {
insertTextAtCursor(textareaEl, ` #${tag} `); insertTextAtCursor(textareaEl.value, ` #${tag} `);
} }
function focus() { function focus() {
if (textareaEl) { if (textareaEl.value) {
textareaEl.focus(); textareaEl.value.focus();
textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length); 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_ => { selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
for (const file of files_) { for (const file of files_) {
files.push(file); files.value.push(file);
} }
}); });
} }
function detachFile(id) { function detachFile(id) {
files = files.filter(x => x.id !== id); files.value = files.value.filter(x => x.id !== id);
} }
function updateFileSensitive(file, sensitive) { function updateFileSensitive(file, sensitive) {
if (props.mock) { if (props.mock) {
emit('fileChangeSensitive', file.id, sensitive); 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) { 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 { 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 { function upload(file: File, name?: string): void {
if (props.mock) return; if (props.mock) return;
uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
files.push(res); files.value.push(res);
}); });
} }
function setVisibility() { function setVisibility() {
if (props.channel) { if (props.channel) {
visibility = 'public'; visibility.value = 'public';
localOnly = true; // TODO: localOnly.value = true; // TODO:
return; return;
} }
os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
currentVisibility: visibility, currentVisibility: visibility.value,
isSilenced: $i?.isSilenced, isSilenced: $i?.isSilenced,
localOnly: localOnly, localOnly: localOnly.value,
src: visibilityButton, src: visibilityButton.value,
}, { }, {
changeVisibility: v => { changeVisibility: v => {
visibility = v; visibility.value = v;
if (defaultStore.state.rememberNoteVisibility) { if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set('visibility', visibility); defaultStore.set('visibility', visibility.value);
} }
}, },
}, 'closed'); }, 'closed');
@ -469,14 +469,14 @@ function setVisibility() {
async function toggleLocalOnly() { async function toggleLocalOnly() {
if (props.channel) { if (props.channel) {
visibility = 'public'; visibility.value = 'public';
localOnly = true; // TODO: localOnly.value = true; // TODO:
return; return;
} }
const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo'); const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo');
if (!localOnly && neverShowInfo !== 'true') { if (!localOnly.value && neverShowInfo !== 'true') {
const confirm = await os.actions({ const confirm = await os.actions({
type: 'question', type: 'question',
title: i18n.ts.disableFederationConfirm, title: i18n.ts.disableFederationConfirm,
@ -506,7 +506,7 @@ async function toggleLocalOnly() {
} }
} }
localOnly = !localOnly; localOnly.value = !localOnly.value;
} }
async function toggleReactionAcceptance() { async function toggleReactionAcceptance() {
@ -519,15 +519,15 @@ async function toggleReactionAcceptance() {
{ value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote },
{ value: 'likeOnly' as const, text: i18n.ts.likeOnly }, { value: 'likeOnly' as const, text: i18n.ts.likeOnly },
], ],
default: reactionAcceptance, default: reactionAcceptance.value,
}); });
if (select.canceled) return; if (select.canceled) return;
reactionAcceptance = select.result; reactionAcceptance.value = select.result;
} }
function pushVisibleUser(user) { function pushVisibleUser(user) {
if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) { if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
visibleUsers.push(user); visibleUsers.value.push(user);
} }
} }
@ -535,34 +535,34 @@ function addVisibleUser() {
os.selectUser().then(user => { os.selectUser().then(user => {
pushVisibleUser(user); pushVisibleUser(user);
if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
text = `@${Misskey.acct.toString(user)} ${text}`; text.value = `@${Misskey.acct.toString(user)} ${text.value}`;
} }
}); });
} }
function removeVisibleUser(user) { function removeVisibleUser(user) {
visibleUsers = erase(user, visibleUsers); visibleUsers.value = erase(user, visibleUsers.value);
} }
function clear() { function clear() {
text = ''; text.value = '';
files = []; files.value = [];
poll = null; poll.value = null;
quoteId = null; quoteId.value = null;
} }
function onKeydown(ev: KeyboardEvent) { 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'); if (ev.key === 'Escape') emit('esc');
} }
function onCompositionUpdate(ev: CompositionEvent) { function onCompositionUpdate(ev: CompositionEvent) {
imeText = ev.data; imeText.value = ev.data;
} }
function onCompositionEnd(ev: CompositionEvent) { function onCompositionEnd(ev: CompositionEvent) {
imeText = ''; imeText.value = '';
} }
async function onPaste(ev: ClipboardEvent) { async function onPaste(ev: ClipboardEvent) {
@ -580,7 +580,7 @@ async function onPaste(ev: ClipboardEvent) {
const paste = ev.clipboardData.getData('text'); 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(); ev.preventDefault();
os.confirm({ os.confirm({
@ -588,11 +588,11 @@ async function onPaste(ev: ClipboardEvent) {
text: i18n.ts.quoteQuestion, text: i18n.ts.quoteQuestion,
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) { if (canceled) {
insertTextAtCursor(textareaEl, paste); insertTextAtCursor(textareaEl.value, paste);
return; 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_; const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) { if (isFile || isDriveFile) {
ev.preventDefault(); ev.preventDefault();
draghover = true; draghover.value = true;
switch (ev.dataTransfer.effectAllowed) { switch (ev.dataTransfer.effectAllowed) {
case 'all': case 'all':
case 'uninitialized': case 'uninitialized':
@ -624,15 +624,15 @@ function onDragover(ev) {
} }
function onDragenter(ev) { function onDragenter(ev) {
draghover = true; draghover.value = true;
} }
function onDragleave(ev) { function onDragleave(ev) {
draghover = false; draghover.value = false;
} }
function onDrop(ev): void { function onDrop(ev): void {
draghover = false; draghover.value = false;
// //
if (ev.dataTransfer.files.length > 0) { if (ev.dataTransfer.files.length > 0) {
@ -645,7 +645,7 @@ function onDrop(ev): void {
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
if (driveFile != null && driveFile !== '') { if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile); const file = JSON.parse(driveFile);
files.push(file); files.value.push(file);
ev.preventDefault(); ev.preventDefault();
} }
//#endregion //#endregion
@ -656,16 +656,16 @@ function saveDraft() {
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
draftData[draftKey] = { draftData[draftKey.value] = {
updatedAt: new Date(), updatedAt: new Date(),
data: { data: {
text: text, text: text.value,
useCw: useCw, useCw: useCw.value,
cw: cw, cw: cw.value,
visibility: visibility, visibility: visibility.value,
localOnly: localOnly, localOnly: localOnly.value,
files: files, files: files.value,
poll: poll, poll: poll.value,
}, },
}; };
@ -675,13 +675,13 @@ function saveDraft() {
function deleteDraft() { function deleteDraft() {
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
delete draftData[draftKey]; delete draftData[draftKey.value];
miLocalStorage.setItem('drafts', JSON.stringify(draftData)); miLocalStorage.setItem('drafts', JSON.stringify(draftData));
} }
async function post(ev?: MouseEvent) { async function post(ev?: MouseEvent) {
if (useCw && (cw == null || cw.trim() === '')) { if (useCw.value && (cw.value == null || cw.value.trim() === '')) {
os.alert({ os.alert({
type: 'error', type: 'error',
text: i18n.ts.cwNotationRequired, text: i18n.ts.cwNotationRequired,
@ -700,13 +700,13 @@ async function post(ev?: MouseEvent) {
if (props.mock) return; if (props.mock) return;
const annoying = const annoying =
text.includes('$[x2') || text.value.includes('$[x2') ||
text.includes('$[x3') || text.value.includes('$[x3') ||
text.includes('$[x4') || text.value.includes('$[x4') ||
text.includes('$[scale') || text.value.includes('$[scale') ||
text.includes('$[position'); text.value.includes('$[position');
if (annoying && visibility === 'public') { if (annoying && visibility.value === 'public') {
const { canceled, result } = await os.actions({ const { canceled, result } = await os.actions({
type: 'warning', type: 'warning',
text: i18n.ts.thisPostMayBeAnnoying, text: i18n.ts.thisPostMayBeAnnoying,
@ -726,26 +726,26 @@ async function post(ev?: MouseEvent) {
if (canceled) return; if (canceled) return;
if (result === 'cancel') return; if (result === 'cancel') return;
if (result === 'home') { if (result === 'home') {
visibility = 'home'; visibility.value = 'home';
} }
} }
let postData = { let postData = {
text: text === '' ? null : text, text: text.value === '' ? null : text.value,
fileIds: files.length > 0 ? files.map(f => f.id) : undefined, fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined,
replyId: props.reply ? props.reply.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, channelId: props.channel ? props.channel.id : undefined,
poll: poll, poll: poll.value,
cw: useCw ? cw ?? '' : null, cw: useCw.value ? cw.value ?? '' : null,
localOnly: localOnly, localOnly: localOnly.value,
visibility: visibility, visibility: visibility.value,
visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined, visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
reactionAcceptance, reactionAcceptance: reactionAcceptance.value,
}; };
if (withHashtags && hashtags && hashtags.trim() !== '') { if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
} }
@ -762,15 +762,15 @@ async function post(ev?: MouseEvent) {
let token = undefined; let token = undefined;
if (postAccount) { if (postAccount.value) {
const storedAccounts = await getAccounts(); 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(() => { os.api('notes/create', postData, token).then(() => {
if (props.freezeAfterPosted) { if (props.freezeAfterPosted) {
posted = true; posted.value = true;
} else { } else {
clear(); clear();
} }
@ -782,8 +782,8 @@ async function post(ev?: MouseEvent) {
const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
} }
posting = false; posting.value = false;
postAccount = null; postAccount.value = null;
incNotesCount(); incNotesCount();
if (notesCount === 1) { if (notesCount === 1) {
@ -828,7 +828,7 @@ async function post(ev?: MouseEvent) {
} }
}); });
}).catch(err => { }).catch(err => {
posting = false; posting.value = false;
os.alert({ os.alert({
type: 'error', type: 'error',
text: err.message + '\n' + (err as any).id, text: err.message + '\n' + (err as any).id,
@ -842,7 +842,7 @@ function cancel() {
function insertMention() { function insertMention() {
os.selectUser().then(user => { 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( emojiPicker.show(
ev.currentTarget ?? ev.target, ev.currentTarget ?? ev.target,
emoji => { emoji => {
insertTextAtCursor(textareaEl, emoji); insertTextAtCursor(textareaEl.value, emoji);
}, },
() => { () => {
textAreaReadOnly.value = false; textAreaReadOnly.value = false;
@ -866,17 +866,17 @@ function showActions(ev) {
text: action.title, text: action.title,
action: () => { action: () => {
action.handler({ action.handler({
text: text, text: text.value,
cw: cw, cw: cw.value,
}, (key, value) => { }, (key, value) => {
if (key === 'text') { text = value; } if (key === 'text') { text.value = value; }
if (key === 'cw') { useCw = value !== null; cw = value; } if (key === 'cw') { useCw.value = value !== null; cw.value = value; }
}); });
}, },
})), ev.currentTarget ?? ev.target); })), ev.currentTarget ?? ev.target);
} }
let postAccount = $ref<Misskey.entities.UserDetailed | null>(null); const postAccount = ref<Misskey.entities.UserDetailed | null>(null);
function openAccountMenu(ev: MouseEvent) { function openAccountMenu(ev: MouseEvent) {
if (props.mock) return; if (props.mock) return;
@ -884,12 +884,12 @@ function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({ openAccountMenu_({
withExtraOperation: false, withExtraOperation: false,
includeCurrentAccount: true, includeCurrentAccount: true,
active: postAccount != null ? postAccount.id : $i.id, active: postAccount.value != null ? postAccount.value.id : $i.id,
onChoose: (account) => { onChoose: (account) => {
if (account.id === $i.id) { if (account.id === $i.id) {
postAccount = null; postAccount.value = null;
} else { } else {
postAccount = account; postAccount.value = account;
} }
}, },
}, ev); }, ev);
@ -905,23 +905,23 @@ onMounted(() => {
} }
// TODO: detach when unmount // TODO: detach when unmount
new Autocomplete(textareaEl, $$(text)); new Autocomplete(textareaEl.value, text);
new Autocomplete(cwInputEl, $$(cw)); new Autocomplete(cwInputEl.value, cw);
new Autocomplete(hashtagsInputEl, $$(hashtags)); new Autocomplete(hashtagsInputEl.value, hashtags);
nextTick(() => { nextTick(() => {
// 稿 // 稿
if (!props.instant && !props.mention && !props.specified && !props.mock) { 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) { if (draft) {
text = draft.data.text; text.value = draft.data.text;
useCw = draft.data.useCw; useCw.value = draft.data.useCw;
cw = draft.data.cw; cw.value = draft.data.cw;
visibility = draft.data.visibility; visibility.value = draft.data.visibility;
localOnly = draft.data.localOnly; localOnly.value = draft.data.localOnly;
files = (draft.data.files || []).filter(draftFile => draftFile); files.value = (draft.data.files || []).filter(draftFile => draftFile);
if (draft.data.poll) { if (draft.data.poll) {
poll = draft.data.poll; poll.value = draft.data.poll;
} }
} }
} }
@ -929,21 +929,21 @@ onMounted(() => {
// //
if (props.initialNote) { if (props.initialNote) {
const init = props.initialNote; const init = props.initialNote;
text = init.text ? init.text : ''; text.value = init.text ? init.text : '';
files = init.files; files.value = init.files;
cw = init.cw; cw.value = init.cw;
useCw = init.cw != null; useCw.value = init.cw != null;
if (init.poll) { if (init.poll) {
poll = { poll.value = {
choices: init.poll.choices.map(x => x.text), choices: init.poll.choices.map(x => x.text),
multiple: init.poll.multiple, multiple: init.poll.multiple,
expiresAt: init.poll.expiresAt, expiresAt: init.poll.expiresAt,
expiredAfter: init.poll.expiredAfter, expiredAfter: init.poll.expiredAfter,
}; };
} }
visibility = init.visibility; visibility.value = init.visibility;
localOnly = init.localOnly; localOnly.value = init.localOnly;
quoteId = init.renote ? init.renote.id : null; quoteId.value = init.renote ? init.renote.id : null;
} }
nextTick(() => watchForDraft()); nextTick(() => watchForDraft());

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { shallowRef } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import MkPostForm from '@/components/MkPostForm.vue'; import MkPostForm from '@/components/MkPostForm.vue';
@ -36,11 +36,11 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let modal = $shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
let form = $shallowRef<InstanceType<typeof MkPostForm>>(); const form = shallowRef<InstanceType<typeof MkPostForm>>();
function onPosted() { function onPosted() {
modal.close({ modal.value.close({
useSendAnimation: true, useSendAnimation: true,
}); });
} }

View File

@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 { deviceKind } from '@/scripts/device-kind.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { getScrollContainer } from '@/scripts/scroll.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_BASE = 1.5;
const PULL_BRAKE_FACTOR = 170; const PULL_BRAKE_FACTOR = 170;
let isPullStart = $ref(false); const isPullStart = ref(false);
let isPullEnd = $ref(false); const isPullEnd = ref(false);
let isRefreshing = $ref(false); const isRefreshing = ref(false);
let pullDistance = $ref(0); const pullDistance = ref(0);
let supportPointerDesktop = false; let supportPointerDesktop = false;
let startScreenY: number | null = null; let startScreenY: number | null = null;
const rootEl = $shallowRef<HTMLDivElement>(); const rootEl = shallowRef<HTMLDivElement>();
let scrollEl: HTMLElement | null = null; let scrollEl: HTMLElement | null = null;
let disabled = false; let disabled = false;
@ -66,17 +66,17 @@ function getScreenY(event) {
} }
function moveStart(event) { function moveStart(event) {
if (!isPullStart && !isRefreshing && !disabled) { if (!isPullStart.value && !isRefreshing.value && !disabled) {
isPullStart = true; isPullStart.value = true;
startScreenY = getScreenY(event); startScreenY = getScreenY(event);
pullDistance = 0; pullDistance.value = 0;
} }
} }
function moveBySystem(to: number): Promise<void> { function moveBySystem(to: number): Promise<void> {
return new Promise(r => { return new Promise(r => {
const startHeight = pullDistance; const startHeight = pullDistance.value;
const overHeight = pullDistance - to; const overHeight = pullDistance.value - to;
if (overHeight < 1) { if (overHeight < 1) {
r(); r();
return; return;
@ -85,36 +85,36 @@ function moveBySystem(to: number): Promise<void> {
let intervalId = setInterval(() => { let intervalId = setInterval(() => {
const time = Date.now() - startTime; const time = Date.now() - startTime;
if (time > RELEASE_TRANSITION_DURATION) { if (time > RELEASE_TRANSITION_DURATION) {
pullDistance = to; pullDistance.value = to;
clearInterval(intervalId); clearInterval(intervalId);
r(); r();
return; return;
} }
const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time; const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time;
if (pullDistance < nextHeight) return; if (pullDistance.value < nextHeight) return;
pullDistance = nextHeight; pullDistance.value = nextHeight;
}, 1); }, 1);
}); });
} }
async function fixOverContent() { async function fixOverContent() {
if (pullDistance > FIRE_THRESHOLD) { if (pullDistance.value > FIRE_THRESHOLD) {
await moveBySystem(FIRE_THRESHOLD); await moveBySystem(FIRE_THRESHOLD);
} }
} }
async function closeContent() { async function closeContent() {
if (pullDistance > 0) { if (pullDistance.value > 0) {
await moveBySystem(0); await moveBySystem(0);
} }
} }
function moveEnd() { function moveEnd() {
if (isPullStart && !isRefreshing) { if (isPullStart.value && !isRefreshing.value) {
startScreenY = null; startScreenY = null;
if (isPullEnd) { if (isPullEnd.value) {
isPullEnd = false; isPullEnd.value = false;
isRefreshing = true; isRefreshing.value = true;
fixOverContent().then(() => { fixOverContent().then(() => {
emit('refresh'); emit('refresh');
props.refresher().then(() => { props.refresher().then(() => {
@ -122,17 +122,17 @@ function moveEnd() {
}); });
}); });
} else { } else {
closeContent().then(() => isPullStart = false); closeContent().then(() => isPullStart.value = false);
} }
} }
} }
function moving(event: TouchEvent | PointerEvent) { 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)) { if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value)) {
pullDistance = 0; pullDistance.value = 0;
isPullEnd = false; isPullEnd.value = false;
moveEnd(); moveEnd();
return; return;
} }
@ -143,13 +143,13 @@ function moving(event: TouchEvent | PointerEvent) {
const moveScreenY = getScreenY(event); const moveScreenY = getScreenY(event);
const moveHeight = moveScreenY - startScreenY!; 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(); 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() { function refreshFinished() {
closeContent().then(() => { closeContent().then(() => {
isPullStart = false; isPullStart.value = false;
isRefreshing = false; isRefreshing.value = false;
}); });
} }
@ -182,26 +182,26 @@ function onScrollContainerScroll() {
} }
function registerEventListenersForReadyToPull() { function registerEventListenersForReadyToPull() {
if (rootEl == null) return; if (rootEl.value == null) return;
rootEl.addEventListener('touchstart', moveStart, { passive: true }); rootEl.value.addEventListener('touchstart', moveStart, { passive: true });
rootEl.addEventListener('touchmove', moving, { passive: false }); // passive: falsepreventDefault使 rootEl.value.addEventListener('touchmove', moving, { passive: false }); // passive: falsepreventDefault使
} }
function unregisterEventListenersForReadyToPull() { function unregisterEventListenersForReadyToPull() {
if (rootEl == null) return; if (rootEl.value == null) return;
rootEl.removeEventListener('touchstart', moveStart); rootEl.value.removeEventListener('touchstart', moveStart);
rootEl.removeEventListener('touchmove', moving); rootEl.value.removeEventListener('touchmove', moving);
} }
onMounted(() => { onMounted(() => {
if (rootEl == null) return; if (rootEl.value == null) return;
scrollEl = getScrollContainer(rootEl); scrollEl = getScrollContainer(rootEl.value);
if (scrollEl == null) return; if (scrollEl == null) return;
scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true }); scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true });
rootEl.addEventListener('touchend', moveEnd, { passive: true }); rootEl.value.addEventListener('touchend', moveEnd, { passive: true });
registerEventListenersForReadyToPull(); registerEventListenersForReadyToPull();
}); });

View File

@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue';
import { $i, getAccounts } from '@/account.js'; import { $i, getAccounts } from '@/account.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { instance } from '@/instance.js'; import { instance } from '@/instance.js';
@ -62,26 +63,26 @@ defineProps<{
}>(); }>();
// ServiceWorker registration // ServiceWorker registration
let registration = $ref<ServiceWorkerRegistration | undefined>(); const registration = ref<ServiceWorkerRegistration | undefined>();
// If this browser supports push notification // If this browser supports push notification
let supported = $ref(false); const supported = ref(false);
// If this browser has already subscribed to push notification // If this browser has already subscribed to push notification
let pushSubscription = $ref<PushSubscription | null>(null); const pushSubscription = ref<PushSubscription | null>(null);
let pushRegistrationInServer = $ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>(); const pushRegistrationInServer = ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>();
function subscribe() { 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 // 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, userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(instance.swPublickey), applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
}) })
.then(async subscription => { .then(async subscription => {
pushSubscription = subscription; pushSubscription.value = subscription;
// Register // Register
pushRegistrationInServer = await api('sw/register', { pushRegistrationInServer.value = await api('sw/register', {
endpoint: subscription.endpoint, endpoint: subscription.endpoint,
auth: encode(subscription.getKey('auth')), auth: encode(subscription.getKey('auth')),
publickey: encode(subscription.getKey('p256dh')), publickey: encode(subscription.getKey('p256dh')),
@ -102,12 +103,12 @@ function subscribe() {
} }
async function unsubscribe() { async function unsubscribe() {
if (!pushSubscription) return; if (!pushSubscription.value) return;
const endpoint = pushSubscription.endpoint; const endpoint = pushSubscription.value.endpoint;
const accounts = await getAccounts(); const accounts = await getAccounts();
pushRegistrationInServer = undefined; pushRegistrationInServer.value = undefined;
if ($i && accounts.length >= 2) { if ($i && accounts.length >= 2) {
apiWithDialog('sw/unregister', { apiWithDialog('sw/unregister', {
@ -115,11 +116,11 @@ async function unsubscribe() {
endpoint, endpoint,
}); });
} else { } else {
pushSubscription.unsubscribe(); pushSubscription.value.unsubscribe();
apiWithDialog('sw/unregister', { apiWithDialog('sw/unregister', {
endpoint, endpoint,
}); });
pushSubscription = null; pushSubscription.value = null;
} }
} }
@ -150,20 +151,20 @@ if (navigator.serviceWorker == null) {
// TODO: // TODO:
} else { } else {
navigator.serviceWorker.ready.then(async swr => { 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) { 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', { const res = await api('sw/show-registration', {
endpoint: pushSubscription.endpoint, endpoint: pushSubscription.value.endpoint,
}); });
if (res) { if (res) {
pushRegistrationInServer = res; pushRegistrationInServer.value = res;
} }
} }
} }
@ -171,6 +172,6 @@ if (navigator.serviceWorker == null) {
} }
defineExpose({ defineExpose({
pushRegistrationInServer: $$(pushRegistrationInServer), pushRegistrationInServer: pushRegistrationInServer,
}); });
</script> </script>

View File

@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
const props = defineProps<{ const props = defineProps<{
modelValue: any; modelValue: any;
@ -36,7 +36,7 @@ const emit = defineEmits<{
(ev: 'update:modelValue', value: any): void; (ev: 'update:modelValue', value: any): void;
}>(); }>();
let checked = $computed(() => props.modelValue === props.value); const checked = computed(() => props.modelValue === props.value);
function toggle(): void { function toggle(): void {
if (props.disabled) return; if (props.disabled) return;

View File

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue';
@ -27,13 +27,13 @@ const emit = defineEmits<{
(ev: 'end'): void; (ev: 'end'): void;
}>(); }>();
let up = $ref(false); const up = ref(false);
const zIndex = os.claimZIndex('middle'); const zIndex = os.claimZIndex('middle');
const angle = (90 - (Math.random() * 180)) + 'deg'; const angle = (90 - (Math.random() * 180)) + 'deg';
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {
up = true; up.value = true;
}, 10); }, 10);
window.setTimeout(() => { window.setTimeout(() => {

View File

@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import * as Misskey from 'misskey-js'; 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 XReaction from '@/components/MkReactionsViewer.reaction.vue';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -38,31 +38,31 @@ const emit = defineEmits<{
const initialReactions = new Set(Object.keys(props.note.reactions)); const initialReactions = new Set(Object.keys(props.note.reactions));
let reactions = $ref<[string, number][]>([]); const reactions = ref<[string, number][]>([]);
let hasMoreReactions = $ref(false); const hasMoreReactions = ref(false);
if (props.note.myReaction && !Object.keys(reactions).includes(props.note.myReaction)) { if (props.note.myReaction && !Object.keys(reactions.value).includes(props.note.myReaction)) {
reactions[props.note.myReaction] = props.note.reactions[props.note.myReaction]; reactions.value[props.note.myReaction] = props.note.reactions[props.note.myReaction];
} }
function onMockToggleReaction(emoji: string, count: number) { function onMockToggleReaction(emoji: string, count: number) {
if (!mock) return; if (!mock) return;
const i = reactions.findIndex((item) => item[0] === emoji); const i = reactions.value.findIndex((item) => item[0] === emoji);
if (i < 0) return; 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]) => { watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {
let newReactions: [string, number][] = []; 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++) { for (let i = 0; i < reactions.value.length; i++) {
const reaction = reactions[i][0]; const reaction = reactions.value[i][0];
if (reaction in newSource && newSource[reaction] !== 0) { if (reaction in newSource && newSource[reaction] !== 0) {
reactions[i][1] = newSource[reaction]; reactions.value[i][1] = newSource[reaction];
newReactions.push(reactions[i]); 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]]); newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]);
} }
reactions = newReactions; reactions.value = newReactions;
}, { immediate: true, deep: true }); }, { immediate: true, deep: true });
</script> </script>

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, nextTick } from 'vue'; import { onMounted, nextTick, shallowRef, ref } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -23,11 +23,11 @@ import { initChart } from '@/scripts/init-chart.js';
initChart(); initChart();
const rootEl = $shallowRef<HTMLDivElement>(null); const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = $shallowRef<HTMLCanvasElement>(null); const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date(); const now = new Date();
let chartInstance: Chart = null; let chartInstance: Chart = null;
let fetching = $ref(true); const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip({ const { handler: externalTooltipHandler } = useChartTooltip({
position: 'middle', position: 'middle',
@ -38,8 +38,8 @@ async function renderChart() {
chartInstance.destroy(); chartInstance.destroy();
} }
const wide = rootEl.offsetWidth > 600; const wide = rootEl.value.offsetWidth > 600;
const narrow = rootEl.offsetWidth < 400; const narrow = rootEl.value.offsetWidth < 400;
const maxDays = wide ? 10 : narrow ? 5 : 7; const maxDays = wide ? 10 : narrow ? 5 : 7;
@ -66,7 +66,7 @@ async function renderChart() {
} }
} }
fetching = false; fetching.value = false;
await nextTick(); await nextTick();
@ -83,7 +83,7 @@ async function renderChart() {
const marginEachCell = 12; const marginEachCell = 12;
chartInstance = new Chart(chartEl, { chartInstance = new Chart(chartEl.value, {
type: 'matrix', type: 'matrix',
data: { data: {
datasets: [{ datasets: [{

View File

@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent, ref } from 'vue';
import { toUnicode } from 'punycode/'; import { toUnicode } from 'punycode/';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill'; 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 { login } from '@/account.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
let signing = $ref(false); const signing = ref(false);
let user = $ref<Misskey.entities.UserDetailed | null>(null); const user = ref<Misskey.entities.UserDetailed | null>(null);
let username = $ref(''); const username = ref('');
let password = $ref(''); const password = ref('');
let token = $ref(''); const token = ref('');
let host = $ref(toUnicode(configHost)); const host = ref(toUnicode(configHost));
let totpLogin = $ref(false); const totpLogin = ref(false);
let queryingKey = $ref(false); const queryingKey = ref(false);
let credentialRequest = $ref<CredentialRequestOptions | null>(null); const credentialRequest = ref<CredentialRequestOptions | null>(null);
let hCaptchaResponse = $ref(null); const hCaptchaResponse = ref(null);
let reCaptchaResponse = $ref(null); const reCaptchaResponse = ref(null);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'login', v: any): void; (ev: 'login', v: any): void;
@ -98,11 +98,11 @@ const props = defineProps({
function onUsernameChange(): void { function onUsernameChange(): void {
os.api('users/show', { os.api('users/show', {
username: username, username: username.value,
}).then(userResponse => { }).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> { async function queryKey(): Promise<void> {
queryingKey = true; queryingKey.value = true;
await webAuthnRequest(credentialRequest) await webAuthnRequest(credentialRequest.value)
.catch(() => { .catch(() => {
queryingKey = false; queryingKey.value = false;
return Promise.reject(null); return Promise.reject(null);
}).then(credential => { }).then(credential => {
credentialRequest = null; credentialRequest.value = null;
queryingKey = false; queryingKey.value = false;
signing = true; signing.value = true;
return os.api('signin', { return os.api('signin', {
username, username: username.value,
password, password: password.value,
credential: credential.toJSON(), credential: credential.toJSON(),
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
}); });
}).then(res => { }).then(res => {
emit('login', res); emit('login', res);
@ -138,39 +138,39 @@ async function queryKey(): Promise<void> {
type: 'error', type: 'error',
text: i18n.ts.signinFailed, text: i18n.ts.signinFailed,
}); });
signing = false; signing.value = false;
}); });
} }
function onSubmit(): void { function onSubmit(): void {
signing = true; signing.value = true;
if (!totpLogin && user && user.twoFactorEnabled) { if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
if (webAuthnSupported() && user.securityKeys) { if (webAuthnSupported() && user.value.securityKeys) {
os.api('signin', { os.api('signin', {
username, username: username.value,
password, password: password.value,
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
}).then(res => { }).then(res => {
totpLogin = true; totpLogin.value = true;
signing = false; signing.value = false;
credentialRequest = parseRequestOptionsFromJSON({ credentialRequest.value = parseRequestOptionsFromJSON({
publicKey: res, publicKey: res,
}); });
}) })
.then(() => queryKey()) .then(() => queryKey())
.catch(loginFailed); .catch(loginFailed);
} else { } else {
totpLogin = true; totpLogin.value = true;
signing = false; signing.value = false;
} }
} else { } else {
os.api('signin', { os.api('signin', {
username, username: username.value,
password, password: password.value,
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
token: user?.twoFactorEnabled ? token : undefined, token: user.value?.twoFactorEnabled ? token.value : undefined,
}).then(res => { }).then(res => {
emit('login', res); emit('login', res);
onLogin(res); onLogin(res);
@ -218,8 +218,8 @@ function loginFailed(err: any): void {
} }
} }
totpLogin = false; totpLogin.value = false;
signing = false; signing.value = false;
} }
function resetPassword(): void { function resetPassword(): void {

View File

@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { shallowRef } from 'vue';
import MkSignin from '@/components/MkSignin.vue'; import MkSignin from '@/components/MkSignin.vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -39,15 +39,15 @@ const emit = defineEmits<{
(ev: 'cancelled'): void; (ev: 'cancelled'): void;
}>(); }>();
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
function onClose() { function onClose() {
emit('cancelled'); emit('cancelled');
if (dialog) dialog.close(); if (dialog.value) dialog.value.close();
} }
function onLogin(res) { function onLogin(res) {
emit('done', res); emit('done', res);
if (dialog) dialog.close(); if (dialog.value) dialog.value.close();
} }
</script> </script>

View File

@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import { toUnicode } from 'punycode/'; import { toUnicode } from 'punycode/';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
import MkInput from './MkInput.vue'; import MkInput from './MkInput.vue';
@ -101,34 +101,34 @@ const emit = defineEmits<{
const host = toUnicode(config.host); const host = toUnicode(config.host);
let hcaptcha = $ref<Captcha | undefined>(); const hcaptcha = ref<Captcha | undefined>();
let recaptcha = $ref<Captcha | undefined>(); const recaptcha = ref<Captcha | undefined>();
let turnstile = $ref<Captcha | undefined>(); const turnstile = ref<Captcha | undefined>();
let username: string = $ref(''); const username = ref<string>('');
let password: string = $ref(''); const password = ref<string>('');
let retypedPassword: string = $ref(''); const retypedPassword = ref<string>('');
let invitationCode: string = $ref(''); const invitationCode = ref<string>('');
let email = $ref(''); const email = ref('');
let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null); const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null);
let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null); const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref(''); const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
let passwordRetypeState: null | 'match' | 'not-match' = $ref(null); const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
let submitting: boolean = $ref(false); const submitting = ref<boolean>(false);
let hCaptchaResponse = $ref(null); const hCaptchaResponse = ref(null);
let reCaptchaResponse = $ref(null); const reCaptchaResponse = ref(null);
let turnstileResponse = $ref(null); const turnstileResponse = ref(null);
let usernameAbortController: null | AbortController = $ref(null); const usernameAbortController = ref<null | AbortController>(null);
let emailAbortController: null | AbortController = $ref(null); const emailAbortController = ref<null | AbortController>(null);
const shouldDisableSubmitting = $computed((): boolean => { const shouldDisableSubmitting = computed((): boolean => {
return submitting || return submitting.value ||
instance.enableHcaptcha && !hCaptchaResponse || instance.enableHcaptcha && !hCaptchaResponse.value ||
instance.enableRecaptcha && !reCaptchaResponse || instance.enableRecaptcha && !reCaptchaResponse.value ||
instance.enableTurnstile && !turnstileResponse || instance.enableTurnstile && !turnstileResponse.value ||
instance.emailRequiredForSignup && emailState !== 'ok' || instance.emailRequiredForSignup && emailState.value !== 'ok' ||
usernameState !== 'ok' || usernameState.value !== 'ok' ||
passwordRetypeState !== 'match'; passwordRetypeState.value !== 'match';
}); });
function getPasswordStrength(source: string): number { function getPasswordStrength(source: string): number {
@ -156,57 +156,57 @@ function getPasswordStrength(source: string): number {
} }
function onChangeUsername(): void { function onChangeUsername(): void {
if (username === '') { if (username.value === '') {
usernameState = null; usernameState.value = null;
return; return;
} }
{ {
const err = const err =
!username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' : !username.value.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
username.length < 1 ? 'min-range' : username.value.length < 1 ? 'min-range' :
username.length > 20 ? 'max-range' : username.value.length > 20 ? 'max-range' :
null; null;
if (err) { if (err) {
usernameState = err; usernameState.value = err;
return; return;
} }
} }
if (usernameAbortController != null) { if (usernameAbortController.value != null) {
usernameAbortController.abort(); usernameAbortController.value.abort();
} }
usernameState = 'wait'; usernameState.value = 'wait';
usernameAbortController = new AbortController(); usernameAbortController.value = new AbortController();
os.api('username/available', { os.api('username/available', {
username, username: username.value,
}, undefined, usernameAbortController.signal).then(result => { }, undefined, usernameAbortController.value.signal).then(result => {
usernameState = result.available ? 'ok' : 'unavailable'; usernameState.value = result.available ? 'ok' : 'unavailable';
}).catch((err) => { }).catch((err) => {
if (err.name !== 'AbortError') { if (err.name !== 'AbortError') {
usernameState = 'error'; usernameState.value = 'error';
} }
}); });
} }
function onChangeEmail(): void { function onChangeEmail(): void {
if (email === '') { if (email.value === '') {
emailState = null; emailState.value = null;
return; return;
} }
if (emailAbortController != null) { if (emailAbortController.value != null) {
emailAbortController.abort(); emailAbortController.value.abort();
} }
emailState = 'wait'; emailState.value = 'wait';
emailAbortController = new AbortController(); emailAbortController.value = new AbortController();
os.api('email-address/available', { os.api('email-address/available', {
emailAddress: email, emailAddress: email.value,
}, undefined, emailAbortController.signal).then(result => { }, undefined, emailAbortController.value.signal).then(result => {
emailState = result.available ? 'ok' : emailState.value = result.available ? 'ok' :
result.reason === 'used' ? 'unavailable:used' : result.reason === 'used' ? 'unavailable:used' :
result.reason === 'format' ? 'unavailable:format' : result.reason === 'format' ? 'unavailable:format' :
result.reason === 'disposable' ? 'unavailable:disposable' : result.reason === 'disposable' ? 'unavailable:disposable' :
@ -215,55 +215,55 @@ function onChangeEmail(): void {
'unavailable'; 'unavailable';
}).catch((err) => { }).catch((err) => {
if (err.name !== 'AbortError') { if (err.name !== 'AbortError') {
emailState = 'error'; emailState.value = 'error';
} }
}); });
} }
function onChangePassword(): void { function onChangePassword(): void {
if (password === '') { if (password.value === '') {
passwordStrength = ''; passwordStrength.value = '';
return; return;
} }
const strength = getPasswordStrength(password); const strength = getPasswordStrength(password.value);
passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; passwordStrength.value = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
} }
function onChangePasswordRetype(): void { function onChangePasswordRetype(): void {
if (retypedPassword === '') { if (retypedPassword.value === '') {
passwordRetypeState = null; passwordRetypeState.value = null;
return; return;
} }
passwordRetypeState = password === retypedPassword ? 'match' : 'not-match'; passwordRetypeState.value = password.value === retypedPassword.value ? 'match' : 'not-match';
} }
async function onSubmit(): Promise<void> { async function onSubmit(): Promise<void> {
if (submitting) return; if (submitting.value) return;
submitting = true; submitting.value = true;
try { try {
await os.api('signup', { await os.api('signup', {
username, username: username.value,
password, password: password.value,
emailAddress: email, emailAddress: email.value,
invitationCode, invitationCode: invitationCode.value,
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
'turnstile-response': turnstileResponse, 'turnstile-response': turnstileResponse.value,
}); });
if (instance.emailRequiredForSignup) { if (instance.emailRequiredForSignup) {
os.alert({ os.alert({
type: 'success', type: 'success',
title: i18n.ts._signup.almostThere, title: i18n.ts._signup.almostThere,
text: i18n.t('_signup.emailSent', { email }), text: i18n.t('_signup.emailSent', { email: email.value }),
}); });
emit('signupEmailPending'); emit('signupEmailPending');
} else { } else {
const res = await os.api('signin', { const res = await os.api('signin', {
username, username: username.value,
password, password: password.value,
}); });
emit('signup', res); emit('signup', res);
@ -272,10 +272,10 @@ async function onSubmit(): Promise<void> {
} }
} }
} catch { } catch {
submitting = false; submitting.value = false;
hcaptcha?.reset?.(); hcaptcha.value?.reset?.();
recaptcha?.reset?.(); recaptcha.value?.reset?.();
turnstile?.reset?.(); turnstile.value?.reset?.();
os.alert({ os.alert({
type: 'error', type: 'error',

View File

@ -33,8 +33,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { shallowRef, ref } from 'vue';
import { $ref } from 'vue/macros';
import XSignup from '@/components/MkSignupDialog.form.vue'; import XSignup from '@/components/MkSignupDialog.form.vue';
import XServerRules from '@/components/MkSignupDialog.rules.vue'; import XServerRules from '@/components/MkSignupDialog.rules.vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
@ -52,17 +52,17 @@ const emit = defineEmits<{
(ev: 'closed'): void; (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) { function onSignup(res) {
emit('done', res); emit('done', res);
dialog.close(); dialog.value.close();
} }
function onSignupEmailPending() { function onSignupEmailPending() {
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkMediaList from '@/components/MkMediaList.vue'; import MkMediaList from '@/components/MkMediaList.vue';
import MkPoll from '@/components/MkPoll.vue'; import MkPoll from '@/components/MkPoll.vue';
@ -44,7 +44,7 @@ const props = defineProps<{
const isLong = shouldCollapsed(props.note, []); const isLong = shouldCollapsed(props.note, []);
const collapsed = $ref(isLong); const collapsed = ref(isLong);
</script> </script>
<style lang="scss" module> <style lang="scss" module>

View File

@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch, onBeforeUnmount } from 'vue'; import { onMounted, watch, onBeforeUnmount, ref, shallowRef } from 'vue';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
const loaded = !!window.TagCanvas; const loaded = !!window.TagCanvas;
@ -23,13 +23,13 @@ const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
const computedStyle = getComputedStyle(document.documentElement); 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 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(''); const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
let available = $ref(false); const available = ref(false);
let rootEl = $shallowRef<HTMLElement | null>(null); const rootEl = shallowRef<HTMLElement | null>(null);
let canvasEl = $shallowRef<HTMLCanvasElement | null>(null); const canvasEl = shallowRef<HTMLCanvasElement | null>(null);
let tagsEl = $shallowRef<HTMLElement | null>(null); const tagsEl = shallowRef<HTMLElement | null>(null);
let width = $ref(300); const width = ref(300);
watch($$(available), () => { watch(available, () => {
try { try {
window.TagCanvas.Start(idForCanvas, idForTags, { window.TagCanvas.Start(idForCanvas, idForTags, {
textColour: '#ffffff', textColour: '#ffffff',
@ -52,15 +52,15 @@ watch($$(available), () => {
}); });
onMounted(() => { onMounted(() => {
width = rootEl.offsetWidth; width.value = rootEl.value.offsetWidth;
if (loaded) { if (loaded) {
available = true; available.value = true;
} else { } else {
document.head.appendChild(Object.assign(document.createElement('script'), { document.head.appendChild(Object.assign(document.createElement('script'), {
async: true, async: true,
src: '/client-assets/tagcanvas.min.js', src: '/client-assets/tagcanvas.min.js',
})).addEventListener('load', () => available = true); })).addEventListener('load', () => available.value = true);
} }
}); });

View File

@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 { Connection } from 'misskey-js/built/streaming.js';
import MkNotes from '@/components/MkNotes.vue'; import MkNotes from '@/components/MkNotes.vue';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
@ -62,8 +62,8 @@ type TimelineQueryType = {
roleId?: string roleId?: string
} }
const prComponent: InstanceType<typeof MkPullToRefresh> = $ref(); const prComponent = ref<InstanceType<typeof MkPullToRefresh>>();
const tlComponent: InstanceType<typeof MkNotes> = $ref(); const tlComponent = ref<InstanceType<typeof MkNotes>>();
let tlNotesCount = 0; let tlNotesCount = 0;
@ -74,7 +74,7 @@ const prepend = note => {
note._shouldInsertAd_ = true; note._shouldInsertAd_ = true;
} }
tlComponent.pagingComponent?.prepend(note); tlComponent.value.pagingComponent?.prepend(note);
emit('note'); emit('note');
@ -248,7 +248,7 @@ function reloadTimeline() {
return new Promise<void>((res) => { return new Promise<void>((res) => {
tlNotesCount = 0; tlNotesCount = 0;
tlComponent.pagingComponent?.reload().then(() => { tlComponent.value.pagingComponent?.reload().then(() => {
res(); res();
}); });
}); });

View File

@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -35,11 +35,11 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex('high'); const zIndex = os.claimZIndex('high');
let showing = $ref(true); const showing = ref(true);
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {
showing = false; showing.value = false;
}, 4000); }, 4000);
}); });
</script> </script>

View File

@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { shallowRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkInput from './MkInput.vue'; import MkInput from './MkInput.vue';
import MkSwitch from './MkSwitch.vue'; import MkSwitch from './MkSwitch.vue';
@ -67,37 +67,37 @@ const emit = defineEmits<{
(ev: 'done', result: { name: string | null, permissions: string[] }): void; (ev: 'done', result: { name: string | null, permissions: string[] }): void;
}>(); }>();
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
let name = $ref(props.initialName); const name = ref(props.initialName);
let permissions = $ref({}); const permissions = ref({});
if (props.initialPermissions) { if (props.initialPermissions) {
for (const kind of props.initialPermissions) { for (const kind of props.initialPermissions) {
permissions[kind] = true; permissions.value[kind] = true;
} }
} else { } else {
for (const kind of Misskey.permissions) { for (const kind of Misskey.permissions) {
permissions[kind] = false; permissions.value[kind] = false;
} }
} }
function ok(): void { function ok(): void {
emit('done', { emit('done', {
name: name, name: name.value,
permissions: Object.keys(permissions).filter(p => permissions[p]), permissions: Object.keys(permissions.value).filter(p => permissions.value[p]),
}); });
dialog.close(); dialog.value.close();
} }
function disableAll(): void { function disableAll(): void {
for (const p in permissions) { for (const p in permissions.value) {
permissions[p] = false; permissions.value[p] = false;
} }
} }
function enableAll(): void { function enableAll(): void {
for (const p in permissions) { for (const p in permissions.value) {
permissions[p] = true; permissions.value[p] = true;
} }
} }
</script> </script>

View File

@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, onUnmounted } from 'vue'; import { defineAsyncComponent, onUnmounted, ref } from 'vue';
import type { summaly } from 'summaly'; import type { summaly } from 'summaly';
import { url as local } from '@/config.js'; import { url as local } from '@/config.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -107,36 +107,36 @@ const props = withDefaults(defineProps<{
}); });
const MOBILE_THRESHOLD = 500; 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 self = props.url.startsWith(local);
const attr = self ? 'to' : 'href'; const attr = self ? 'to' : 'href';
const target = self ? null : '_blank'; const target = self ? null : '_blank';
let fetching = $ref(true); const fetching = ref(true);
let title = $ref<string | null>(null); const title = ref<string | null>(null);
let description = $ref<string | null>(null); const description = ref<string | null>(null);
let thumbnail = $ref<string | null>(null); const thumbnail = ref<string | null>(null);
let icon = $ref<string | null>(null); const icon = ref<string | null>(null);
let sitename = $ref<string | null>(null); const sitename = ref<string | null>(null);
let sensitive = $ref<boolean>(false); const sensitive = ref<boolean>(false);
let player = $ref({ const player = ref({
url: null, url: null,
width: null, width: null,
height: null, height: null,
} as SummalyResult['player']); } as SummalyResult['player']);
let playerEnabled = $ref(false); const playerEnabled = ref(false);
let tweetId = $ref<string | null>(null); const tweetId = ref<string | null>(null);
let tweetExpanded = $ref(props.detail); const tweetExpanded = ref(props.detail);
const embedId = `embed${Math.random().toString().replace(/\D/, '')}`; const embedId = `embed${Math.random().toString().replace(/\D/, '')}`;
let tweetHeight = $ref(150); const tweetHeight = ref(150);
let unknownUrl = $ref(false); const unknownUrl = ref(false);
const requestUrl = new URL(props.url); const requestUrl = new URL(props.url);
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid 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') { 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+)/); 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)')) { 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}`) window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`)
.then(res => { .then(res => {
if (!res.ok) { if (!res.ok) {
fetching = false; fetching.value = false;
unknownUrl = true; unknownUrl.value = true;
return; return;
} }
@ -157,21 +157,21 @@ window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLa
}) })
.then((info: SummalyResult) => { .then((info: SummalyResult) => {
if (info.url == null) { if (info.url == null) {
fetching = false; fetching.value = false;
unknownUrl = true; unknownUrl.value = true;
return; return;
} }
fetching = false; fetching.value = false;
unknownUrl = false; unknownUrl.value = false;
title = info.title; title.value = info.title;
description = info.description; description.value = info.description;
thumbnail = info.thumbnail; thumbnail.value = info.thumbnail;
icon = info.icon; icon.value = info.icon;
sitename = info.sitename; sitename.value = info.sitename;
player = info.player; player.value = info.player;
sensitive = info.sensitive ?? false; sensitive.value = info.sensitive ?? false;
}); });
function adjustTweetHeight(message: any) { function adjustTweetHeight(message: any) {
@ -180,7 +180,7 @@ function adjustTweetHeight(message: any) {
if (embed?.method !== 'twttr.private.resize') return; if (embed?.method !== 'twttr.private.resize') return;
if (embed?.id !== embedId) return; if (embed?.id !== embedId) return;
const height = embed?.params[0]?.height; const height = embed?.params[0]?.height;
if (height) tweetHeight = height; if (height) tweetHeight.value = height;
} }
const openPlayer = (): void => { const openPlayer = (): void => {

View File

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -28,16 +28,16 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex('middle'); const zIndex = os.claimZIndex('middle');
let top = $ref(0); const top = ref(0);
let left = $ref(0); const left = ref(0);
onMounted(() => { onMounted(() => {
const rect = props.source.getBoundingClientRect(); const rect = props.source.getBoundingClientRect();
const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset; const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
const y = rect.top + props.source.offsetHeight + window.pageYOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset;
top = y; top.value = y;
left = x; left.value = x;
}); });
</script> </script>

View File

@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@ -66,12 +66,12 @@ const props = defineProps<{
announcement?: any, announcement?: any,
}>(); }>();
let dialog = $ref(null); const dialog = ref(null);
let title: string = $ref(props.announcement ? props.announcement.title : ''); const title = ref<string>(props.announcement ? props.announcement.title : '');
let text: string = $ref(props.announcement ? props.announcement.text : ''); const text = ref<string>(props.announcement ? props.announcement.text : '');
let icon: string = $ref(props.announcement ? props.announcement.icon : 'info'); const icon = ref<string>(props.announcement ? props.announcement.icon : 'info');
let display: string = $ref(props.announcement ? props.announcement.display : 'dialog'); const display = ref<string>(props.announcement ? props.announcement.display : 'dialog');
let needConfirmationToRead = $ref(props.announcement ? props.announcement.needConfirmationToRead : false); const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
@ -80,12 +80,12 @@ const emit = defineEmits<{
async function done() { async function done() {
const params = { const params = {
title: title, title: title.value,
text: text, text: text.value,
icon: icon, icon: icon.value,
imageUrl: null, imageUrl: null,
display: display, display: display.value,
needConfirmationToRead: needConfirmationToRead, needConfirmationToRead: needConfirmationToRead.value,
userId: props.user.id, userId: props.user.id,
}; };
@ -102,7 +102,7 @@ async function done() {
}, },
}); });
dialog.close(); dialog.value.close();
} else { } else {
const created = await os.apiWithDialog('admin/announcements/create', params); const created = await os.apiWithDialog('admin/announcements/create', params);
@ -110,14 +110,14 @@ async function done() {
created: created, created: created,
}); });
dialog.close(); dialog.value.close();
} }
} }
async function del() { async function del() {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.t('removeAreYouSure', { x: title }), text: i18n.t('removeAreYouSure', { x: title.value }),
}); });
if (canceled) return; if (canceled) return;
@ -127,7 +127,7 @@ async function del() {
emit('done', { emit('done', {
deleted: true, deleted: true,
}); });
dialog.close(); dialog.value.close();
}); });
} }
</script> </script>

View File

@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import MkMiniChart from '@/components/MkMiniChart.vue'; import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { acct } from '@/filters/user.js'; import { acct } from '@/filters/user.js';
@ -28,14 +28,14 @@ const props = withDefaults(defineProps<{
withChart: true, withChart: true,
}); });
let chartValues = $ref<number[] | null>(null); const chartValues = ref<number[] | null>(null);
onMounted(() => { onMounted(() => {
if (props.withChart) { if (props.withChart) {
os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => {
// //
res.inc.splice(0, 1); res.inc.splice(0, 1);
chartValues = res.inc; chartValues.value = res.inc;
}); });
} }
}); });

View File

@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -24,7 +24,7 @@ const props = defineProps<{
user: Misskey.entities.User; user: Misskey.entities.User;
}>(); }>();
const text = $computed(() => { const text = computed(() => {
switch (props.user.onlineStatus) { switch (props.user.onlineStatus) {
case 'online': return i18n.ts.online; case 'online': return i18n.ts.online;
case 'active': return i18n.ts.active; case 'active': return i18n.ts.active;

View File

@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkFollowButton from '@/components/MkFollowButton.vue'; import MkFollowButton from '@/components/MkFollowButton.vue';
import { userPage } from '@/filters/user.js'; import { userPage } from '@/filters/user.js';
@ -80,18 +80,18 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex('middle'); const zIndex = os.claimZIndex('middle');
let user = $ref<Misskey.entities.UserDetailed | null>(null); const user = ref<Misskey.entities.UserDetailed | null>(null);
let top = $ref(0); const top = ref(0);
let left = $ref(0); const left = ref(0);
function showMenu(ev: MouseEvent) { function showMenu(ev: MouseEvent) {
const { menu, cleanup } = getUserMenu(user); const { menu, cleanup } = getUserMenu(user.value);
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
} }
onMounted(() => { onMounted(() => {
if (typeof props.q === 'object') { if (typeof props.q === 'object') {
user = props.q; user.value = props.q;
} else { } else {
const query = props.q.startsWith('@') ? const query = props.q.startsWith('@') ?
Misskey.acct.parse(props.q.substring(1)) : Misskey.acct.parse(props.q.substring(1)) :
@ -99,7 +99,7 @@ onMounted(() => {
os.api('users/show', query).then(res => { os.api('users/show', query).then(res => {
if (!props.showing) return; 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 x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
const y = rect.top + props.source.offsetHeight + window.pageYOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset;
top = y; top.value = y;
left = x; left.value = x;
}); });
</script> </script>

View File

@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import FormSplit from '@/components/form/split.vue'; import FormSplit from '@/components/form/split.vue';
@ -78,43 +78,43 @@ const props = defineProps<{
includeSelf?: boolean; includeSelf?: boolean;
}>(); }>();
let username = $ref(''); const username = ref('');
let host = $ref(''); const host = ref('');
let users: Misskey.entities.UserDetailed[] = $ref([]); const users = ref<Misskey.entities.UserDetailed[]>([]);
let recentUsers: Misskey.entities.UserDetailed[] = $ref([]); const recentUsers = ref<Misskey.entities.UserDetailed[]>([]);
let selected: Misskey.entities.UserDetailed | null = $ref(null); const selected = ref<Misskey.entities.UserDetailed | null>(null);
let dialogEl = $ref(); const dialogEl = ref();
const search = () => { const search = () => {
if (username === '' && host === '') { if (username.value === '' && host.value === '') {
users = []; users.value = [];
return; return;
} }
os.api('users/search-by-username-and-host', { os.api('users/search-by-username-and-host', {
username: username, username: username.value,
host: host, host: host.value,
limit: 10, limit: 10,
detail: false, detail: false,
}).then(_users => { }).then(_users => {
users = _users; users.value = _users;
}); });
}; };
const ok = () => { const ok = () => {
if (selected == null) return; if (selected.value == null) return;
emit('ok', selected); emit('ok', selected.value);
dialogEl.close(); dialogEl.value.close();
// 使 // 使
let recents = defaultStore.state.recentlyUsedUsers; let recents = defaultStore.state.recentlyUsedUsers;
recents = recents.filter(x => x !== selected.id); recents = recents.filter(x => x !== selected.value.id);
recents.unshift(selected.id); recents.unshift(selected.value.id);
defaultStore.set('recentlyUsedUsers', recents.splice(0, 16)); defaultStore.set('recentlyUsedUsers', recents.splice(0, 16));
}; };
const cancel = () => { const cancel = () => {
emit('cancel'); emit('cancel');
dialogEl.close(); dialogEl.value.close();
}; };
onMounted(() => { onMounted(() => {
@ -122,9 +122,9 @@ onMounted(() => {
userIds: defaultStore.state.recentlyUsedUsers, userIds: defaultStore.state.recentlyUsedUsers,
}).then(users => { }).then(users => {
if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) { if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) {
recentUsers = [$i, ...users]; recentUsers.value = [$i, ...users];
} else { } else {
recentUsers = users; recentUsers.value = users;
} }
}); });
}); });

View File

@ -53,10 +53,10 @@ import MkFolder from '@/components/MkFolder.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { $i } from '@/account.js'; import { $i } from '@/account.js';
let isLocked = ref(false); const isLocked = ref(false);
let hideOnlineStatus = ref(false); const hideOnlineStatus = ref(false);
let noCrawle = ref(false); const noCrawle = ref(false);
let preventAiLearning = ref(true); const preventAiLearning = ref(true);
watch([isLocked, hideOnlineStatus, noCrawle, preventAiLearning], () => { watch([isLocked, hideOnlineStatus, noCrawle, preventAiLearning], () => {
os.api('i/update', { os.api('i/update', {

View File

@ -42,12 +42,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick } from 'vue'; import { nextTick, shallowRef, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
const modal = $shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
currentVisibility: typeof Misskey.noteVisibilities[number]; currentVisibility: typeof Misskey.noteVisibilities[number];
@ -62,13 +62,13 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let v = $ref(props.currentVisibility); const v = ref(props.currentVisibility);
function choose(visibility: typeof Misskey.noteVisibilities[number]): void { function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
v = visibility; v.value = visibility;
emit('changeVisibility', visibility); emit('changeVisibility', visibility);
nextTick(() => { nextTick(() => {
if (modal) modal.close(); if (modal.value) modal.value.close();
}); });
} }
</script> </script>

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, shallowRef, ref } from 'vue';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient'; import gradient from 'chartjs-plugin-gradient';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
@ -25,11 +25,11 @@ import { initChart } from '@/scripts/init-chart.js';
initChart(); initChart();
const chartEl = $shallowRef<HTMLCanvasElement>(null); const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date(); const now = new Date();
let chartInstance: Chart = null; let chartInstance: Chart = null;
const chartLimit = 30; const chartLimit = 30;
let fetching = $ref(true); const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip(); const { handler: externalTooltipHandler } = useChartTooltip();
@ -65,7 +65,7 @@ async function renderChart() {
const max = Math.max(...raw.read); const max = Math.max(...raw.read);
chartInstance = new Chart(chartEl, { chartInstance = new Chart(chartEl.value, {
type: 'bar', type: 'bar',
data: { data: {
datasets: [{ datasets: [{
@ -147,7 +147,7 @@ async function renderChart() {
plugins: [chartVLine(vLineColor)], plugins: [chartVLine(vLineColor)],
}); });
fetching = false; fetching.value = false;
} }
onMounted(async () => { onMounted(async () => {

View File

@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import XTimeline from './welcome.timeline.vue'; import XTimeline from './welcome.timeline.vue';
import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSigninDialog from '@/components/MkSigninDialog.vue';
@ -67,15 +67,15 @@ import number from '@/filters/number.js';
import MkNumber from '@/components/MkNumber.vue'; import MkNumber from '@/components/MkNumber.vue';
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue'; import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
let meta = $ref<Misskey.entities.MetaResponse | null>(null); const meta = ref<Misskey.entities.MetaResponse | null>(null);
let stats = $ref<Misskey.entities.StatsResponse | null>(null); const stats = ref<Misskey.entities.StatsResponse | null>(null);
os.api('meta', { detail: true }).then(_meta => { os.api('meta', { detail: true }).then(_meta => {
meta = _meta; meta.value = _meta;
}); });
os.api('stats', {}).then((res) => { os.api('stats', {}).then((res) => {
stats = res; stats.value = res;
}); });
function signin() { function signin() {

View File

@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 contains from '@/scripts/contains.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
@ -107,18 +107,18 @@ const emit = defineEmits<{
provide('inWindow', true); provide('inWindow', true);
let rootEl = $shallowRef<HTMLElement | null>(); const rootEl = shallowRef<HTMLElement | null>();
let showing = $ref(true); const showing = ref(true);
let beforeClickedAt = 0; let beforeClickedAt = 0;
let maximized = $ref(false); const maximized = ref(false);
let minimized = $ref(false); const minimized = ref(false);
let unResizedTop = ''; let unResizedTop = '';
let unResizedLeft = ''; let unResizedLeft = '';
let unResizedWidth = ''; let unResizedWidth = '';
let unResizedHeight = ''; let unResizedHeight = '';
function close() { function close() {
showing = false; showing.value = false;
} }
function onKeydown(evt) { function onKeydown(evt) {
@ -137,46 +137,46 @@ function onContextmenu(ev: MouseEvent) {
// //
function top() { function top() {
if (rootEl) { if (rootEl.value) {
rootEl.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low'); rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low');
} }
} }
function maximize() { function maximize() {
maximized = true; maximized.value = true;
unResizedTop = rootEl.style.top; unResizedTop = rootEl.value.style.top;
unResizedLeft = rootEl.style.left; unResizedLeft = rootEl.value.style.left;
unResizedWidth = rootEl.style.width; unResizedWidth = rootEl.value.style.width;
unResizedHeight = rootEl.style.height; unResizedHeight = rootEl.value.style.height;
rootEl.style.top = '0'; rootEl.value.style.top = '0';
rootEl.style.left = '0'; rootEl.value.style.left = '0';
rootEl.style.width = '100%'; rootEl.value.style.width = '100%';
rootEl.style.height = '100%'; rootEl.value.style.height = '100%';
} }
function unMaximize() { function unMaximize() {
maximized = false; maximized.value = false;
rootEl.style.top = unResizedTop; rootEl.value.style.top = unResizedTop;
rootEl.style.left = unResizedLeft; rootEl.value.style.left = unResizedLeft;
rootEl.style.width = unResizedWidth; rootEl.value.style.width = unResizedWidth;
rootEl.style.height = unResizedHeight; rootEl.value.style.height = unResizedHeight;
} }
function minimize() { function minimize() {
minimized = true; minimized.value = true;
unResizedWidth = rootEl.style.width; unResizedWidth = rootEl.value.style.width;
unResizedHeight = rootEl.style.height; unResizedHeight = rootEl.value.style.height;
rootEl.style.width = minWidth + 'px'; rootEl.value.style.width = minWidth + 'px';
rootEl.style.height = props.mini ? '32px' : '39px'; rootEl.value.style.height = props.mini ? '32px' : '39px';
} }
function unMinimize() { function unMinimize() {
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
minimized = false; minimized.value = false;
rootEl.style.width = unResizedWidth; rootEl.value.style.width = unResizedWidth;
rootEl.style.height = unResizedHeight; rootEl.value.style.height = unResizedHeight;
const browserWidth = window.innerWidth; const browserWidth = window.innerWidth;
const browserHeight = window.innerHeight; const browserHeight = window.innerHeight;
const windowWidth = main.offsetWidth; const windowWidth = main.offsetWidth;
@ -192,7 +192,7 @@ function onBodyMousedown() {
} }
function onDblClick() { function onDblClick() {
if (minimized) { if (minimized.value) {
unMinimize(); unMinimize();
} else { } else {
maximize(); maximize();
@ -205,7 +205,7 @@ function onHeaderMousedown(evt: MouseEvent) {
let beforeMaximized = false; let beforeMaximized = false;
if (maximized) { if (maximized.value) {
beforeMaximized = true; beforeMaximized = true;
unMaximize(); unMaximize();
} }
@ -219,7 +219,7 @@ function onHeaderMousedown(evt: MouseEvent) {
beforeClickedAt = Date.now(); beforeClickedAt = Date.now();
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
if (!contains(main, document.activeElement)) main.focus(); if (!contains(main, document.activeElement)) main.focus();
@ -251,8 +251,8 @@ function onHeaderMousedown(evt: MouseEvent) {
// //
if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth; if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
rootEl.style.left = moveLeft + 'px'; rootEl.value.style.left = moveLeft + 'px';
rootEl.style.top = moveTop + 'px'; rootEl.value.style.top = moveTop + 'px';
} }
if (beforeMaximized) { if (beforeMaximized) {
@ -270,7 +270,7 @@ function onHeaderMousedown(evt: MouseEvent) {
// //
function onTopHandleMousedown(evt) { function onTopHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
// null // null
if (main == null) return; if (main == null) return;
@ -298,7 +298,7 @@ function onTopHandleMousedown(evt) {
// //
function onRightHandleMousedown(evt) { function onRightHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
const base = evt.clientX; const base = evt.clientX;
@ -323,7 +323,7 @@ function onRightHandleMousedown(evt) {
// //
function onBottomHandleMousedown(evt) { function onBottomHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
const base = evt.clientY; const base = evt.clientY;
@ -348,7 +348,7 @@ function onBottomHandleMousedown(evt) {
// //
function onLeftHandleMousedown(evt) { function onLeftHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
const base = evt.clientX; const base = evt.clientX;
@ -400,27 +400,27 @@ function onBottomLeftHandleMousedown(evt) {
// //
function applyTransformHeight(height) { function applyTransformHeight(height) {
if (height > window.innerHeight) height = window.innerHeight; if (height > window.innerHeight) height = window.innerHeight;
rootEl.style.height = height + 'px'; rootEl.value.style.height = height + 'px';
} }
// //
function applyTransformWidth(width) { function applyTransformWidth(width) {
if (width > window.innerWidth) width = window.innerWidth; if (width > window.innerWidth) width = window.innerWidth;
rootEl.style.width = width + 'px'; rootEl.value.style.width = width + 'px';
} }
// Y // Y
function applyTransformTop(top) { function applyTransformTop(top) {
rootEl.style.top = top + 'px'; rootEl.value.style.top = top + 'px';
} }
// X // X
function applyTransformLeft(left) { function applyTransformLeft(left) {
rootEl.style.left = left + 'px'; rootEl.value.style.left = left + 'px';
} }
function onBrowserResize() { function onBrowserResize() {
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
const position = main.getBoundingClientRect(); const position = main.getBoundingClientRect();
@ -438,8 +438,8 @@ onMounted(() => {
applyTransformWidth(props.initialWidth); applyTransformWidth(props.initialWidth);
if (props.initialHeight) applyTransformHeight(props.initialHeight); if (props.initialHeight) applyTransformHeight(props.initialHeight);
applyTransformTop((window.innerHeight / 2) - (rootEl.offsetHeight / 2)); applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2));
applyTransformLeft((window.innerWidth / 2) - (rootEl.offsetWidth / 2)); applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2));
// //
top(); top();

View File

@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import MkWindow from '@/components/MkWindow.vue'; import MkWindow from '@/components/MkWindow.vue';
import { versatileLang } from '@/scripts/intl-const.js'; import { versatileLang } from '@/scripts/intl-const.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -35,22 +36,22 @@ const props = defineProps<{
const requestUrl = new URL(props.url); const requestUrl = new URL(props.url);
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
let fetching = $ref(true); const fetching = ref(true);
let title = $ref<string | null>(null); const title = ref<string | null>(null);
let player = $ref({ const player = ref({
url: null, url: null,
width: null, width: null,
height: null, height: null,
}); });
const ytFetch = (): void => { const ytFetch = (): void => {
fetching = true; fetching.value = true;
window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`).then(res => { window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`).then(res => {
res.json().then(info => { res.json().then(info => {
if (info.url == null) return; if (info.url == null) return;
title = info.title; title.value = info.title;
fetching = false; fetching.value = false;
player = info.player; player.value = info.player;
}); });
}); });
}; };

View File

@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { url } from '@/config.js'; import { url } from '@/config.js';
@ -28,7 +29,7 @@ const props = withDefaults(defineProps<{
const router = useRouter(); const router = useRouter();
const active = $computed(() => { const active = computed(() => {
if (props.activeClass == null) return false; if (props.activeClass == null) return false;
const resolved = router.resolve(props.to); const resolved = router.resolve(props.to);
if (resolved == null) return false; if (resolved == null) return false;

View File

@ -96,7 +96,7 @@ const choseAd = (): Ad | null => {
}; };
const chosen = ref(choseAd()); 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 { function reduceFrequency(): void {
if (chosen.value == null) return; if (chosen.value == null) return;

View File

@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from 'vue'; import { watch, ref, computed } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkImgWithBlurhash from '../MkImgWithBlurhash.vue'; import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
import MkA from './MkA.vue'; import MkA from './MkA.vue';
@ -47,9 +47,9 @@ import { acct, userPage } from '@/filters/user.js';
import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue'; import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
const animation = $ref(defaultStore.state.animation); const animation = ref(defaultStore.state.animation);
const squareAvatars = $ref(defaultStore.state.squareAvatars); const squareAvatars = ref(defaultStore.state.squareAvatars);
const useBlurEffect = $ref(defaultStore.state.useBlurEffect); const useBlurEffect = ref(defaultStore.state.useBlurEffect);
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
user: Misskey.entities.User; user: Misskey.entities.User;
@ -79,11 +79,11 @@ const emit = defineEmits<{
const showDecoration = props.forceShowDecoration || defaultStore.state.showAvatarDecorations; const showDecoration = props.forceShowDecoration || defaultStore.state.showAvatarDecorations;
const bound = $computed(() => props.link const bound = computed(() => props.link
? { to: userPage(props.user), target: props.target } ? { 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) ? getStaticImageUrl(props.user.avatarUrl)
: props.user.avatarUrl); : props.user.avatarUrl);
@ -116,10 +116,10 @@ function getDecorationScale() {
return scaleX === 1 ? undefined : `${scaleX} 1`; return scaleX === 1 ? undefined : `${scaleX} 1`;
} }
let color = $ref<string | undefined>(); const color = ref<string | undefined>();
watch(() => props.user.avatarBlurhash, () => { watch(() => props.user.avatarBlurhash, () => {
color = extractAvgColorFromBlurhash(props.user.avatarBlurhash); color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
}, { }, {
immediate: true, immediate: true,
}); });

View File

@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject } from 'vue'; import { computed, inject, ref } from 'vue';
import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js'; import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
import { customEmojisMap } from '@/custom-emojis.js'; import { customEmojisMap } from '@/custom-emojis.js';
@ -71,7 +71,7 @@ const url = computed(() => {
}); });
const alt = computed(() => `:${customEmojiName.value}:`); const alt = computed(() => `:${customEmojiName.value}:`);
let errored = $ref(url.value == null); const errored = ref(url.value == null);
function onClick(ev: MouseEvent) { function onClick(ev: MouseEvent) {
if (props.menu) { if (props.menu) {

View File

@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 tinycolor from 'tinycolor2';
import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import XTabs, { Tab } from './MkPageHeader.tabs.vue';
import { scrollToTop } from '@/scripts/scroll.js'; import { scrollToTop } from '@/scripts/scroll.js';
@ -69,13 +69,13 @@ const metadata = injectPageMetadata();
const hideTitle = inject('shouldOmitHeaderTitle', false); const hideTitle = inject('shouldOmitHeaderTitle', false);
const thin_ = props.thin || inject('shouldHeaderThin', 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); const bg = ref<string | undefined>(undefined);
let narrow = $ref(false); const narrow = ref(false);
const hasTabs = $computed(() => props.tabs.length > 0); const hasTabs = computed(() => props.tabs.length > 0);
const hasActions = $computed(() => props.actions && props.actions.length > 0); const hasActions = computed(() => props.actions && props.actions.length > 0);
const show = $computed(() => { const show = computed(() => {
return !hideTitle || hasTabs || hasActions; return !hideTitle || hasTabs.value || hasActions.value;
}); });
const preventDrag = (ev: TouchEvent) => { const preventDrag = (ev: TouchEvent) => {
@ -83,8 +83,8 @@ const preventDrag = (ev: TouchEvent) => {
}; };
const top = () => { const top = () => {
if (el) { if (el.value) {
scrollToTop(el as HTMLElement, { behavior: 'smooth' }); scrollToTop(el.value as HTMLElement, { behavior: 'smooth' });
} }
}; };
@ -111,14 +111,14 @@ onMounted(() => {
calcBg(); calcBg();
globalEvents.on('themeChanged', calcBg); globalEvents.on('themeChanged', calcBg);
if (el && el.parentElement) { if (el.value && el.value.parentElement) {
narrow = el.parentElement.offsetWidth < 500; narrow.value = el.value.parentElement.offsetWidth < 500;
ro = new ResizeObserver((entries, observer) => { ro = new ResizeObserver((entries, observer) => {
if (el && el.parentElement && document.body.contains(el as HTMLElement)) { if (el.value && el.value.parentElement && document.body.contains(el.value as HTMLElement)) {
narrow = el.parentElement.offsetWidth < 500; narrow.value = el.value.parentElement.offsetWidth < 500;
} }
}); });
ro.observe(el.parentElement as HTMLElement); ro.observe(el.value.parentElement as HTMLElement);
} }
}); });

View File

@ -18,36 +18,36 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue'; import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
import { $$ } from 'vue/macros';
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const'; import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const';
const rootEl = $shallowRef<HTMLElement>(); const rootEl = shallowRef<HTMLElement>();
const headerEl = $shallowRef<HTMLElement>(); const headerEl = shallowRef<HTMLElement>();
const footerEl = $shallowRef<HTMLElement>(); const footerEl = shallowRef<HTMLElement>();
const bodyEl = $shallowRef<HTMLElement>(); const bodyEl = shallowRef<HTMLElement>();
let headerHeight = $ref<string | undefined>(); const headerHeight = ref<string | undefined>();
let childStickyTop = $ref(0); const childStickyTop = ref(0);
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, 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>(); const footerHeight = ref<string | undefined>();
let childStickyBottom = $ref(0); const childStickyBottom = ref(0);
const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0)); const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0));
provide(CURRENT_STICKY_BOTTOM, $$(childStickyBottom)); provide(CURRENT_STICKY_BOTTOM, childStickyBottom);
const calc = () => { const calc = () => {
// KeepAlive null // KeepAlive null
if (headerEl != null) { if (headerEl.value != null) {
childStickyTop = parentStickyTop.value + headerEl.offsetHeight; childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
headerHeight = headerEl.offsetHeight.toString(); headerHeight.value = headerEl.value.offsetHeight.toString();
} }
// KeepAlive null // KeepAlive null
if (footerEl != null) { if (footerEl.value != null) {
childStickyBottom = parentStickyBottom.value + footerEl.offsetHeight; childStickyBottom.value = parentStickyBottom.value + footerEl.value.offsetHeight;
footerHeight = footerEl.offsetHeight.toString(); footerHeight.value = footerEl.value.offsetHeight.toString();
} }
}; };
@ -62,28 +62,28 @@ onMounted(() => {
watch([parentStickyTop, parentStickyBottom], calc); watch([parentStickyTop, parentStickyBottom], calc);
watch($$(childStickyTop), () => { watch(childStickyTop, () => {
bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`); bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`);
}, { }, {
immediate: true, immediate: true,
}); });
watch($$(childStickyBottom), () => { watch(childStickyBottom, () => {
bodyEl.style.setProperty('--stickyBottom', `${childStickyBottom}px`); bodyEl.value.style.setProperty('--stickyBottom', `${childStickyBottom.value}px`);
}, { }, {
immediate: true, immediate: true,
}); });
headerEl.style.position = 'sticky'; headerEl.value.style.position = 'sticky';
headerEl.style.top = 'var(--stickyTop, 0)'; headerEl.value.style.top = 'var(--stickyTop, 0)';
headerEl.style.zIndex = '1000'; headerEl.value.style.zIndex = '1000';
footerEl.style.position = 'sticky'; footerEl.value.style.position = 'sticky';
footerEl.style.bottom = 'var(--stickyBottom, 0)'; footerEl.value.style.bottom = 'var(--stickyBottom, 0)';
footerEl.style.zIndex = '1000'; footerEl.value.style.zIndex = '1000';
observer.observe(headerEl); observer.observe(headerEl.value);
observer.observe(footerEl); observer.observe(footerEl.value);
}); });
onUnmounted(() => { onUnmounted(() => {
@ -91,6 +91,6 @@ onUnmounted(() => {
}); });
defineExpose({ defineExpose({
rootEl: $$(rootEl), rootEl: rootEl,
}); });
</script> </script>

View File

@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import isChromatic from 'chromatic/isChromatic'; import isChromatic from 'chromatic/isChromatic';
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted, ref, computed } from 'vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { dateTimeFormat } from '@/scripts/intl-const.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; const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
// eslint-disable-next-line vue/no-setup-props-destructure // eslint-disable-next-line vue/no-setup-props-destructure
let now = $ref((props.origin ?? new Date()).getTime()); const now = ref((props.origin ?? new Date()).getTime());
const ago = $computed(() => (now - _time) / 1000/*ms*/); const ago = computed(() => (now.value - _time) / 1000/*ms*/);
const relative = $computed<string>(() => { const relative = computed<string>(() => {
if (props.mode === 'absolute') return ''; // absoluterelative使 if (props.mode === 'absolute') return ''; // absoluterelative使
if (invalid) return i18n.ts._ago.invalid; if (invalid) return i18n.ts._ago.invalid;
return ( return (
ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) : ago.value >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago.value / 31536000).toString() }) :
ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) : ago.value >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago.value / 2592000).toString() }) :
ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) : ago.value >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago.value / 604800).toString() }) :
ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) : ago.value >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago.value / 86400).toString() }) :
ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) : ago.value >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago.value / 3600).toString() }) :
ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) : ago.value >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago.value / 60)).toString() }) :
ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) : ago.value >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago.value % 60)).toString() }) :
ago >= -3 ? i18n.ts._ago.justNow : ago.value >= -3 ? i18n.ts._ago.justNow :
ago < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago / 31536000).toString() }) : ago.value < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago.value / 31536000).toString() }) :
ago < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago / 2592000).toString() }) : ago.value < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago.value / 2592000).toString() }) :
ago < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago / 604800).toString() }) : ago.value < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago.value / 604800).toString() }) :
ago < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago / 86400).toString() }) : ago.value < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago.value / 86400).toString() }) :
ago < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago / 3600).toString() }) : ago.value < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago.value / 3600).toString() }) :
ago < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago / 60)).toString() }) : ago.value < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago.value / 60)).toString() }) :
i18n.t('_timeIn.seconds', { n: (~~(-ago % 60)).toString() }) i18n.t('_timeIn.seconds', { n: (~~(-ago.value % 60)).toString() })
); );
}); });
@ -77,8 +77,8 @@ let tickId: number;
let currentInterval: number; let currentInterval: number;
function tick() { function tick() {
now = (new Date()).getTime(); now.value = (new Date()).getTime();
const nextInterval = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000; const nextInterval = ago.value < 60 ? 10000 : ago.value < 3600 ? 60000 : 180000;
if (currentInterval !== nextInterval) { if (currentInterval !== nextInterval) {
if (tickId) window.clearInterval(tickId); if (tickId) window.clearInterval(tickId);

View File

@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onBeforeUnmount, provide } from 'vue'; import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue';
import { Resolved, Router } from '@/nirax'; import { Resolved, Router } from '@/nirax';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
@ -46,16 +46,16 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
} }
const current = resolveNested(router.current)!; const current = resolveNested(router.current)!;
let currentPageComponent = $shallowRef(current.route.component); const currentPageComponent = shallowRef(current.route.component);
let currentPageProps = $ref(current.props); const currentPageProps = ref(current.props);
let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props))); const key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
function onChange({ resolved, key: newKey }) { function onChange({ resolved, key: newKey }) {
const current = resolveNested(resolved); const current = resolveNested(resolved);
if (current == null) return; if (current == null) return;
currentPageComponent = current.route.component; currentPageComponent.value = current.route.component;
currentPageProps = current.props; currentPageProps.value = current.props;
key = current.route.path + JSON.stringify(Object.fromEntries(current.props)); key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props));
} }
router.addListener('change', onChange); router.addListener('change', onChange);

View File

@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { version } from '@/config.js'; import { version } from '@/config.js';
@ -42,29 +42,29 @@ const props = withDefaults(defineProps<{
}>(), { }>(), {
}); });
let loaded = $ref(false); const loaded = ref(false);
let serverIsDead = $ref(false); const serverIsDead = ref(false);
let meta = $ref<Misskey.entities.MetaResponse | null>(null); const meta = ref<Misskey.entities.MetaResponse | null>(null);
os.api('meta', { os.api('meta', {
detail: false, detail: false,
}).then(res => { }).then(res => {
loaded = true; loaded.value = true;
serverIsDead = false; serverIsDead.value = false;
meta = res; meta.value = res;
miLocalStorage.setItem('v', res.version); miLocalStorage.setItem('v', res.version);
}, () => { }, () => {
loaded = true; loaded.value = true;
serverIsDead = true; serverIsDead.value = true;
}); });
function reload() { function reload() {
unisonReload(); unisonReload();
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.error, title: i18n.ts.error,

View File

@ -123,7 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onBeforeUnmount } from 'vue'; import { nextTick, onBeforeUnmount, ref, shallowRef, computed } from 'vue';
import { version } from '@/config.js'; import { version } from '@/config.js';
import FormLink from '@/components/form/link.vue'; import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue'; import FormSection from '@/components/form/section.vue';
@ -310,18 +310,18 @@ const patrons = [
'SHO SEKIGUCHI', 'SHO SEKIGUCHI',
]; ];
let thereIsTreasure = $ref($i && !claimedAchievements.includes('foundTreasure')); const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
let easterEggReady = false; let easterEggReady = false;
let easterEggEmojis = $ref([]); const easterEggEmojis = ref([]);
let easterEggEngine = $ref(null); const easterEggEngine = ref(null);
const containerEl = $shallowRef<HTMLElement>(); const containerEl = shallowRef<HTMLElement>();
function iconLoaded() { function iconLoaded() {
const emojis = defaultStore.state.reactions; const emojis = defaultStore.state.reactions;
const containerWidth = containerEl.offsetWidth; const containerWidth = containerEl.value.offsetWidth;
for (let i = 0; i < 32; i++) { for (let i = 0; i < 32; i++) {
easterEggEmojis.push({ easterEggEmojis.value.push({
id: i.toString(), id: i.toString(),
top: -(128 + (Math.random() * 256)), top: -(128 + (Math.random() * 256)),
left: (Math.random() * containerWidth), left: (Math.random() * containerWidth),
@ -337,7 +337,7 @@ function iconLoaded() {
function gravity() { function gravity() {
if (!easterEggReady) return; if (!easterEggReady) return;
easterEggReady = false; easterEggReady = false;
easterEggEngine = physics(containerEl); easterEggEngine.value = physics(containerEl.value);
} }
function iLoveMisskey() { function iLoveMisskey() {
@ -348,19 +348,19 @@ function iLoveMisskey() {
} }
function getTreasure() { function getTreasure() {
thereIsTreasure = false; thereIsTreasure.value = false;
claimAchievement('foundTreasure'); claimAchievement('foundTreasure');
} }
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (easterEggEngine) { if (easterEggEngine.value) {
easterEggEngine.stop(); easterEggEngine.value.stop();
} }
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.aboutMisskey, title: i18n.ts.aboutMisskey,

View File

@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from 'vue'; import { watch, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import XEmoji from './emojis.emoji.vue'; import XEmoji from './emojis.emoji.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@ -47,44 +47,44 @@ import { i18n } from '@/i18n.js';
import { $i } from '@/account.js'; import { $i } from '@/account.js';
const customEmojiTags = getCustomEmojiTags(); const customEmojiTags = getCustomEmojiTags();
let q = $ref(''); const q = ref('');
let searchEmojis = $ref<Misskey.entities.EmojiSimple[]>(null); const searchEmojis = ref<Misskey.entities.EmojiSimple[]>(null);
let selectedTags = $ref(new Set()); const selectedTags = ref(new Set());
function search() { function search() {
if ((q === '' || q == null) && selectedTags.size === 0) { if ((q.value === '' || q.value == null) && selectedTags.value.size === 0) {
searchEmojis = null; searchEmojis.value = null;
return; return;
} }
if (selectedTags.size === 0) { if (selectedTags.value.size === 0) {
const queryarry = q.match(/\:([a-z0-9_]*)\:/g); const queryarry = q.value.match(/\:([a-z0-9_]*)\:/g);
if (queryarry) { if (queryarry) {
searchEmojis = customEmojis.value.filter(emoji => searchEmojis.value = customEmojis.value.filter(emoji =>
queryarry.includes(`:${emoji.name}:`), queryarry.includes(`:${emoji.name}:`),
); );
} else { } else {
searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q)); searchEmojis.value = customEmojis.value.filter(emoji => emoji.name.includes(q.value) || emoji.aliases.includes(q.value));
} }
} else { } else {
searchEmojis = customEmojis.value.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t))); searchEmojis.value = customEmojis.value.filter(emoji => (emoji.name.includes(q.value) || emoji.aliases.includes(q.value)) && [...selectedTags.value].every(t => emoji.aliases.includes(t)));
} }
} }
function toggleTag(tag) { function toggleTag(tag) {
if (selectedTags.has(tag)) { if (selectedTags.value.has(tag)) {
selectedTags.delete(tag); selectedTags.value.delete(tag);
} else { } else {
selectedTags.add(tag); selectedTags.value.add(tag);
} }
} }
watch($$(q), () => { watch(q, () => {
search(); search();
}); });
watch($$(selectedTags), () => { watch(selectedTags, () => {
search(); search();
}, { deep: true }); }, { deep: true });
</script> </script>

View File

@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue';
@ -59,25 +59,25 @@ import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
import FormSplit from '@/components/form/split.vue'; import FormSplit from '@/components/form/split.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
let host = $ref(''); const host = ref('');
let state = $ref('federating'); const state = ref('federating');
let sort = $ref('+pubSub'); const sort = ref('+pubSub');
const pagination = { const pagination = {
endpoint: 'federation/instances' as const, endpoint: 'federation/instances' as const,
limit: 10, limit: 10,
displayLimit: 50, displayLimit: 50,
offsetMode: true, offsetMode: true,
params: computed(() => ({ params: computed(() => ({
sort: sort, sort: sort.value,
host: host !== '' ? host : null, host: host.value !== '' ? host.value : null,
...( ...(
state === 'federating' ? { federating: true } : state.value === 'federating' ? { federating: true } :
state === 'subscribing' ? { subscribing: true } : state.value === 'subscribing' ? { subscribing: true } :
state === 'publishing' ? { publishing: true } : state.value === 'publishing' ? { publishing: true } :
state === 'suspended' ? { suspended: true } : state.value === 'suspended' ? { suspended: true } :
state === 'blocked' ? { blocked: true } : state.value === 'blocked' ? { blocked: true } :
state === 'silenced' ? { silenced: true } : state.value === 'silenced' ? { silenced: true } :
state === 'notResponding' ? { notResponding: true } : state.value === 'notResponding' ? { notResponding: true } :
{}), {}),
})), })),
} as Paging; } as Paging;

View File

@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch } from 'vue'; import { computed, watch, ref } from 'vue';
import XEmojis from './about.emojis.vue'; import XEmojis from './about.emojis.vue';
import XFederation from './about.federation.vue'; import XFederation from './about.federation.vue';
import { version, host } from '@/config.js'; import { version, host } from '@/config.js';
@ -126,23 +126,23 @@ const props = withDefaults(defineProps<{
initialTab: 'overview', initialTab: 'overview',
}); });
let stats = $ref(null); const stats = ref(null);
let tab = $ref(props.initialTab); const tab = ref(props.initialTab);
watch($$(tab), () => { watch(tab, () => {
if (tab === 'charts') { if (tab.value === 'charts') {
claimAchievement('viewInstanceChart'); claimAchievement('viewInstanceChart');
} }
}); });
const initStats = () => os.api('stats', { const initStats = () => os.api('stats', {
}).then((res) => { }).then((res) => {
stats = res; stats.value = res;
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'overview', key: 'overview',
title: i18n.ts.overview, title: i18n.ts.overview,
}, { }, {

View File

@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import MkObjectView from '@/components/MkObjectView.vue'; import MkObjectView from '@/components/MkObjectView.vue';
@ -82,19 +82,19 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
import { iAmAdmin, iAmModerator } from '@/account.js'; import { iAmAdmin, iAmModerator } from '@/account.js';
let tab = $ref('overview'); const tab = ref('overview');
let file: any = $ref(null); const file = ref<any>(null);
let info: any = $ref(null); const info = ref<any>(null);
let isSensitive: boolean = $ref(false); const isSensitive = ref<boolean>(false);
const props = defineProps<{ const props = defineProps<{
fileId: string, fileId: string,
}>(); }>();
async function fetch() { async function fetch() {
file = await os.api('drive/files/show', { fileId: props.fileId }); file.value = await os.api('drive/files/show', { fileId: props.fileId });
info = await os.api('admin/drive/show-file', { fileId: props.fileId }); info.value = await os.api('admin/drive/show-file', { fileId: props.fileId });
isSensitive = file.isSensitive; isSensitive.value = file.value.isSensitive;
} }
fetch(); fetch();
@ -102,29 +102,29 @@ fetch();
async function del() { async function del() {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.t('removeAreYouSure', { x: file.name }), text: i18n.t('removeAreYouSure', { x: file.value.name }),
}); });
if (canceled) return; if (canceled) return;
os.apiWithDialog('drive/files/delete', { os.apiWithDialog('drive/files/delete', {
fileId: file.id, fileId: file.value.id,
}); });
} }
async function toggleIsSensitive(v) { async function toggleIsSensitive(v) {
await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v }); await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
isSensitive = v; isSensitive.value = v;
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
icon: 'ti ti-external-link', icon: 'ti ti-external-link',
handler: () => { handler: () => {
window.open(file.url, '_blank'); window.open(file.value.url, '_blank');
}, },
}]); }]);
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'overview', key: 'overview',
title: i18n.ts.overview, title: i18n.ts.overview,
icon: 'ti ti-info-circle', icon: 'ti ti-info-circle',
@ -139,7 +139,7 @@ const headerTabs = $computed(() => [{
}]); }]);
definePageMetadata(computed(() => ({ definePageMetadata(computed(() => ({
title: file ? i18n.ts.file + ': ' + file.name : i18n.ts.file, title: file.value ? i18n.ts.file + ': ' + file.value.name : i18n.ts.file,
icon: 'ti ti-file', icon: 'ti ti-file',
}))); })));
</script> </script>

View File

@ -203,7 +203,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, watch } from 'vue'; import { computed, defineAsyncComponent, watch, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkChart from '@/components/MkChart.vue'; import MkChart from '@/components/MkChart.vue';
import MkObjectView from '@/components/MkObjectView.vue'; import MkObjectView from '@/components/MkObjectView.vue';
@ -234,17 +234,17 @@ const props = withDefaults(defineProps<{
initialTab: 'overview', initialTab: 'overview',
}); });
let tab = $ref(props.initialTab); const tab = ref(props.initialTab);
let chartSrc = $ref('per-user-notes'); const chartSrc = ref('per-user-notes');
let user = $ref<null | Misskey.entities.UserDetailed>(); const user = ref<null | Misskey.entities.UserDetailed>();
let init = $ref<ReturnType<typeof createFetcher>>(); const init = ref<ReturnType<typeof createFetcher>>();
let info = $ref(); const info = ref();
let ips = $ref(null); const ips = ref(null);
let ap = $ref(null); const ap = ref(null);
let moderator = $ref(false); const moderator = ref(false);
let silenced = $ref(false); const silenced = ref(false);
let suspended = $ref(false); const suspended = ref(false);
let moderationNote = $ref(''); const moderationNote = ref('');
const filesPagination = { const filesPagination = {
endpoint: 'admin/drive/files' as const, endpoint: 'admin/drive/files' as const,
limit: 10, limit: 10,
@ -259,7 +259,7 @@ const announcementsPagination = {
userId: props.userId, userId: props.userId,
})), })),
}; };
let expandedRoles = $ref([]); const expandedRoles = ref([]);
function createFetcher() { function createFetcher() {
return () => Promise.all([os.api('users/show', { return () => Promise.all([os.api('users/show', {
@ -269,27 +269,27 @@ function createFetcher() {
}), iAmAdmin ? os.api('admin/get-user-ips', { }), iAmAdmin ? os.api('admin/get-user-ips', {
userId: props.userId, userId: props.userId,
}) : Promise.resolve(null)]).then(([_user, _info, _ips]) => { }) : Promise.resolve(null)]).then(([_user, _info, _ips]) => {
user = _user; user.value = _user;
info = _info; info.value = _info;
ips = _ips; ips.value = _ips;
moderator = info.isModerator; moderator.value = info.value.isModerator;
silenced = info.isSilenced; silenced.value = info.value.isSilenced;
suspended = info.isSuspended; suspended.value = info.value.isSuspended;
moderationNote = info.moderationNote; moderationNote.value = info.value.moderationNote;
watch($$(moderationNote), async () => { watch(moderationNote, async () => {
await os.api('admin/update-user-note', { userId: user.id, text: moderationNote }); await os.api('admin/update-user-note', { userId: user.value.id, text: moderationNote.value });
await refreshUser(); await refreshUser();
}); });
}); });
} }
function refreshUser() { function refreshUser() {
init = createFetcher(); init.value = createFetcher();
} }
async function updateRemoteUser() { async function updateRemoteUser() {
await os.apiWithDialog('federation/update-remote-user', { userId: user.id }); await os.apiWithDialog('federation/update-remote-user', { userId: user.value.id });
refreshUser(); refreshUser();
} }
@ -302,7 +302,7 @@ async function resetPassword() {
return; return;
} else { } else {
const { password } = await os.api('admin/reset-password', { const { password } = await os.api('admin/reset-password', {
userId: user.id, userId: user.value.id,
}); });
os.alert({ os.alert({
type: 'success', type: 'success',
@ -317,9 +317,9 @@ async function toggleSuspend(v) {
text: v ? i18n.ts.suspendConfirm : i18n.ts.unsuspendConfirm, text: v ? i18n.ts.suspendConfirm : i18n.ts.unsuspendConfirm,
}); });
if (confirm.canceled) { if (confirm.canceled) {
suspended = !v; suspended.value = !v;
} else { } else {
await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.id }); await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.value.id });
await refreshUser(); await refreshUser();
} }
} }
@ -331,7 +331,7 @@ async function unsetUserAvatar() {
}); });
if (confirm.canceled) return; if (confirm.canceled) return;
const process = async () => { const process = async () => {
await os.api('admin/unset-user-avatar', { userId: user.id }); await os.api('admin/unset-user-avatar', { userId: user.value.id });
os.success(); os.success();
}; };
await process().catch(err => { await process().catch(err => {
@ -350,7 +350,7 @@ async function unsetUserBanner() {
}); });
if (confirm.canceled) return; if (confirm.canceled) return;
const process = async () => { const process = async () => {
await os.api('admin/unset-user-banner', { userId: user.id }); await os.api('admin/unset-user-banner', { userId: user.value.id });
os.success(); os.success();
}; };
await process().catch(err => { await process().catch(err => {
@ -369,7 +369,7 @@ async function deleteAllFiles() {
}); });
if (confirm.canceled) return; if (confirm.canceled) return;
const process = async () => { const process = async () => {
await os.api('admin/delete-all-files-of-a-user', { userId: user.id }); await os.api('admin/delete-all-files-of-a-user', { userId: user.value.id });
os.success(); os.success();
}; };
await process().catch(err => { await process().catch(err => {
@ -389,13 +389,13 @@ async function deleteAccount() {
if (confirm.canceled) return; if (confirm.canceled) return;
const typed = await os.inputText({ const typed = await os.inputText({
text: i18n.t('typeToConfirm', { x: user?.username }), text: i18n.t('typeToConfirm', { x: user.value?.username }),
}); });
if (typed.canceled) return; if (typed.canceled) return;
if (typed.result === user?.username) { if (typed.result === user.value?.username) {
await os.apiWithDialog('admin/delete-account', { await os.apiWithDialog('admin/delete-account', {
userId: user.id, userId: user.value.id,
}); });
} else { } else {
os.alert({ os.alert({
@ -438,7 +438,7 @@ async function assignRole() {
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30) : period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
: null; : null;
await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.id, expiresAt }); await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.value.id, expiresAt });
refreshUser(); refreshUser();
} }
@ -448,50 +448,50 @@ async function unassignRole(role, ev) {
icon: 'ti ti-x', icon: 'ti ti-x',
danger: true, danger: true,
action: async () => { action: async () => {
await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.id }); await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.value.id });
refreshUser(); refreshUser();
}, },
}], ev.currentTarget ?? ev.target); }], ev.currentTarget ?? ev.target);
} }
function toggleRoleItem(role) { function toggleRoleItem(role) {
if (expandedRoles.includes(role.id)) { if (expandedRoles.value.includes(role.id)) {
expandedRoles = expandedRoles.filter(x => x !== role.id); expandedRoles.value = expandedRoles.value.filter(x => x !== role.id);
} else { } else {
expandedRoles.push(role.id); expandedRoles.value.push(role.id);
} }
} }
function createAnnouncement() { function createAnnouncement() {
os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), { os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
user, user: user.value,
}, {}, 'closed'); }, {}, 'closed');
} }
function editAnnouncement(announcement) { function editAnnouncement(announcement) {
os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), { os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
user, user: user.value,
announcement, announcement,
}, {}, 'closed'); }, {}, 'closed');
} }
watch(() => props.userId, () => { watch(() => props.userId, () => {
init = createFetcher(); init.value = createFetcher();
}, { }, {
immediate: true, immediate: true,
}); });
watch($$(user), () => { watch(user, () => {
os.api('ap/get', { os.api('ap/get', {
uri: user.uri ?? `${url}/users/${user.id}`, uri: user.value.uri ?? `${url}/users/${user.value.id}`,
}).then(res => { }).then(res => {
ap = res; ap.value = res;
}); });
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'overview', key: 'overview',
title: i18n.ts.overview, title: i18n.ts.overview,
icon: 'ti ti-info-circle', icon: 'ti ti-info-circle',
@ -518,7 +518,7 @@ const headerTabs = $computed(() => [{
}]); }]);
definePageMetadata(computed(() => ({ definePageMetadata(computed(() => ({
title: user ? acct(user) : i18n.ts.userInfo, title: user.value ? acct(user.value) : i18n.ts.userInfo,
icon: 'ti ti-user-exclamation', icon: 'ti ti-user-exclamation',
}))); })));
</script> </script>

View File

@ -69,7 +69,7 @@ const metadata = injectPageMetadata();
const el = shallowRef<HTMLElement>(null); const el = shallowRef<HTMLElement>(null);
const tabRefs = {}; const tabRefs = {};
const tabHighlightEl = $shallowRef<HTMLElement | null>(null); const tabHighlightEl = shallowRef<HTMLElement | null>(null);
const bg = ref(null); const bg = ref(null);
const height = ref(0); const height = ref(0);
const hasTabs = computed(() => { const hasTabs = computed(() => {
@ -131,13 +131,13 @@ onMounted(() => {
watch(() => [props.tab, props.tabs], () => { watch(() => [props.tab, props.tabs], () => {
nextTick(() => { nextTick(() => {
const tabEl = tabRefs[props.tab]; const tabEl = tabRefs[props.tab];
if (tabEl && tabHighlightEl) { if (tabEl && tabHighlightEl.value) {
// offsetWidth offsetLeft getBoundingClientRect 使 // offsetWidth offsetLeft getBoundingClientRect 使
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
const parentRect = tabEl.parentElement.getBoundingClientRect(); const parentRect = tabEl.parentElement.getBoundingClientRect();
const rect = tabEl.getBoundingClientRect(); const rect = tabEl.getBoundingClientRect();
tabHighlightEl.style.width = rect.width + 'px'; tabHighlightEl.value.style.width = rect.width + 'px';
tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px'; tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
} }
}); });
}, { }, {

View File

@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, shallowRef, ref } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
@ -61,31 +61,31 @@ import XAbuseReport from '@/components/MkAbuseReport.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
let reports = $shallowRef<InstanceType<typeof MkPagination>>(); const reports = shallowRef<InstanceType<typeof MkPagination>>();
let state = $ref('unresolved'); const state = ref('unresolved');
let reporterOrigin = $ref('combined'); const reporterOrigin = ref('combined');
let targetUserOrigin = $ref('combined'); const targetUserOrigin = ref('combined');
let searchUsername = $ref(''); const searchUsername = ref('');
let searchHost = $ref(''); const searchHost = ref('');
const pagination = { const pagination = {
endpoint: 'admin/abuse-user-reports' as const, endpoint: 'admin/abuse-user-reports' as const,
limit: 10, limit: 10,
params: computed(() => ({ params: computed(() => ({
state, state: state.value,
reporterOrigin, reporterOrigin: reporterOrigin.value,
targetUserOrigin, targetUserOrigin: targetUserOrigin.value,
})), })),
}; };
function resolved(reportId) { function resolved(reportId) {
reports.removeItem(reportId); reports.value.removeItem(reportId);
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.abuseReports, title: i18n.ts.abuseReports,

View File

@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
@ -98,7 +98,7 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
let ads: any[] = $ref([]); const ads = ref<any[]>([]);
// ISOTZUTCTZ // ISOTZUTCTZ
const localTime = new Date(); const localTime = new Date();
@ -109,7 +109,7 @@ let publishing: boolean | null = null;
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => { os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
if (adsResponse != null) { if (adsResponse != null) {
ads = adsResponse.map(r => { ads.value = adsResponse.map(r => {
const exdate = new Date(r.expiresAt); const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt); const stdate = new Date(r.startsAt);
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff); exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@ -141,7 +141,7 @@ function toggleDayOfWeek(ad, index) {
} }
function add() { function add() {
ads.unshift({ ads.value.unshift({
id: null, id: null,
memo: '', memo: '',
place: 'square', place: 'square',
@ -161,7 +161,7 @@ function remove(ad) {
text: i18n.t('removeAreYouSure', { x: ad.url }), text: i18n.t('removeAreYouSure', { x: ad.url }),
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; if (canceled) return;
ads = ads.filter(x => x !== ad); ads.value = ads.value.filter(x => x !== ad);
if (ad.id == null) return; if (ad.id == null) return;
os.apiWithDialog('admin/ad/delete', { os.apiWithDialog('admin/ad/delete', {
id: ad.id, id: ad.id,
@ -209,9 +209,9 @@ function save(ad) {
} }
function more() { function more() {
os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => { os.api('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
if (adsResponse == null) return; if (adsResponse == null) return;
ads = ads.concat(adsResponse.map(r => { ads.value = ads.value.concat(adsResponse.map(r => {
const exdate = new Date(r.expiresAt); const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt); const stdate = new Date(r.startsAt);
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff); exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@ -228,7 +228,7 @@ function more() {
function refresh() { function refresh() {
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => { os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
if (adsResponse == null) return; if (adsResponse == null) return;
ads = adsResponse.map(r => { ads.value = adsResponse.map(r => {
const exdate = new Date(r.expiresAt); const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt); const stdate = new Date(r.startsAt);
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff); exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@ -244,14 +244,14 @@ function refresh() {
refresh(); refresh();
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'ti ti-plus', icon: 'ti ti-plus',
text: i18n.ts.add, text: i18n.ts.add,
handler: add, handler: add,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.ads, title: i18n.ts.ads,

View File

@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
@ -84,14 +84,14 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkFolder from '@/components/MkFolder.vue'; import MkFolder from '@/components/MkFolder.vue';
let announcements: any[] = $ref([]); const announcements = ref<any[]>([]);
os.api('admin/announcements/list').then(announcementResponse => { os.api('admin/announcements/list').then(announcementResponse => {
announcements = announcementResponse; announcements.value = announcementResponse;
}); });
function add() { function add() {
announcements.unshift({ announcements.value.unshift({
_id: Math.random().toString(36), _id: Math.random().toString(36),
id: null, id: null,
title: 'New announcement', title: 'New announcement',
@ -111,7 +111,7 @@ function del(announcement) {
text: i18n.t('deleteAreYouSure', { x: announcement.title }), text: i18n.t('deleteAreYouSure', { x: announcement.title }),
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; if (canceled) return;
announcements = announcements.filter(x => x !== announcement); announcements.value = announcements.value.filter(x => x !== announcement);
os.api('admin/announcements/delete', announcement); os.api('admin/announcements/delete', announcement);
}); });
} }
@ -134,27 +134,27 @@ async function save(announcement) {
} }
function more() { function more() {
os.api('admin/announcements/list', { untilId: announcements.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => { os.api('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
announcements = announcements.concat(announcementResponse); announcements.value = announcements.value.concat(announcementResponse);
}); });
} }
function refresh() { function refresh() {
os.api('admin/announcements/list').then(announcementResponse => { os.api('admin/announcements/list').then(announcementResponse => {
announcements = announcementResponse; announcements.value = announcementResponse;
}); });
} }
refresh(); refresh();
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'ti ti-plus', icon: 'ti ti-plus',
text: i18n.ts.add, text: i18n.ts.add,
handler: add, handler: add,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.announcements, title: i18n.ts.announcements,

View File

@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent, ref } from 'vue';
import MkRadios from '@/components/MkRadios.vue'; import MkRadios from '@/components/MkRadios.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@ -76,37 +76,37 @@ import { i18n } from '@/i18n.js';
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue')); const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
let provider = $ref(null); const provider = ref(null);
let hcaptchaSiteKey: string | null = $ref(null); const hcaptchaSiteKey = ref<string | null>(null);
let hcaptchaSecretKey: string | null = $ref(null); const hcaptchaSecretKey = ref<string | null>(null);
let recaptchaSiteKey: string | null = $ref(null); const recaptchaSiteKey = ref<string | null>(null);
let recaptchaSecretKey: string | null = $ref(null); const recaptchaSecretKey = ref<string | null>(null);
let turnstileSiteKey: string | null = $ref(null); const turnstileSiteKey = ref<string | null>(null);
let turnstileSecretKey: string | null = $ref(null); const turnstileSecretKey = ref<string | null>(null);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
hcaptchaSiteKey = meta.hcaptchaSiteKey; hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
hcaptchaSecretKey = meta.hcaptchaSecretKey; hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
recaptchaSiteKey = meta.recaptchaSiteKey; recaptchaSiteKey.value = meta.recaptchaSiteKey;
recaptchaSecretKey = meta.recaptchaSecretKey; recaptchaSecretKey.value = meta.recaptchaSecretKey;
turnstileSiteKey = meta.turnstileSiteKey; turnstileSiteKey.value = meta.turnstileSiteKey;
turnstileSecretKey = meta.turnstileSecretKey; turnstileSecretKey.value = meta.turnstileSecretKey;
provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null; provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null;
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
enableHcaptcha: provider === 'hcaptcha', enableHcaptcha: provider.value === 'hcaptcha',
hcaptchaSiteKey, hcaptchaSiteKey: hcaptchaSiteKey.value,
hcaptchaSecretKey, hcaptchaSecretKey: hcaptchaSecretKey.value,
enableRecaptcha: provider === 'recaptcha', enableRecaptcha: provider.value === 'recaptcha',
recaptchaSiteKey, recaptchaSiteKey: recaptchaSiteKey.value,
recaptchaSecretKey, recaptchaSecretKey: recaptchaSecretKey.value,
enableTurnstile: provider === 'turnstile', enableTurnstile: provider.value === 'turnstile',
turnstileSiteKey, turnstileSiteKey: turnstileSiteKey.value,
turnstileSecretKey, turnstileSecretKey: turnstileSecretKey.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import JSON5 from 'json5'; import JSON5 from 'json5';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
@ -111,55 +111,55 @@ import MkButton from '@/components/MkButton.vue';
import MkColorInput from '@/components/MkColorInput.vue'; import MkColorInput from '@/components/MkColorInput.vue';
import { host } from '@/config.js'; import { host } from '@/config.js';
let iconUrl: string | null = $ref(null); const iconUrl = ref<string | null>(null);
let app192IconUrl: string | null = $ref(null); const app192IconUrl = ref<string | null>(null);
let app512IconUrl: string | null = $ref(null); const app512IconUrl = ref<string | null>(null);
let bannerUrl: string | null = $ref(null); const bannerUrl = ref<string | null>(null);
let backgroundImageUrl: string | null = $ref(null); const backgroundImageUrl = ref<string | null>(null);
let themeColor: any = $ref(null); const themeColor = ref<any>(null);
let defaultLightTheme: any = $ref(null); const defaultLightTheme = ref<any>(null);
let defaultDarkTheme: any = $ref(null); const defaultDarkTheme = ref<any>(null);
let serverErrorImageUrl: string | null = $ref(null); const serverErrorImageUrl = ref<string | null>(null);
let infoImageUrl: string | null = $ref(null); const infoImageUrl = ref<string | null>(null);
let notFoundImageUrl: string | null = $ref(null); const notFoundImageUrl = ref<string | null>(null);
let manifestJsonOverride: string = $ref('{}'); const manifestJsonOverride = ref<string>('{}');
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
iconUrl = meta.iconUrl; iconUrl.value = meta.iconUrl;
app192IconUrl = meta.app192IconUrl; app192IconUrl.value = meta.app192IconUrl;
app512IconUrl = meta.app512IconUrl; app512IconUrl.value = meta.app512IconUrl;
bannerUrl = meta.bannerUrl; bannerUrl.value = meta.bannerUrl;
backgroundImageUrl = meta.backgroundImageUrl; backgroundImageUrl.value = meta.backgroundImageUrl;
themeColor = meta.themeColor; themeColor.value = meta.themeColor;
defaultLightTheme = meta.defaultLightTheme; defaultLightTheme.value = meta.defaultLightTheme;
defaultDarkTheme = meta.defaultDarkTheme; defaultDarkTheme.value = meta.defaultDarkTheme;
serverErrorImageUrl = meta.serverErrorImageUrl; serverErrorImageUrl.value = meta.serverErrorImageUrl;
infoImageUrl = meta.infoImageUrl; infoImageUrl.value = meta.infoImageUrl;
notFoundImageUrl = meta.notFoundImageUrl; notFoundImageUrl.value = meta.notFoundImageUrl;
manifestJsonOverride = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'); manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
iconUrl, iconUrl: iconUrl.value,
app192IconUrl, app192IconUrl: app192IconUrl.value,
app512IconUrl, app512IconUrl: app512IconUrl.value,
bannerUrl, bannerUrl: bannerUrl.value,
backgroundImageUrl, backgroundImageUrl: backgroundImageUrl.value,
themeColor: themeColor === '' ? null : themeColor, themeColor: themeColor.value === '' ? null : themeColor.value,
defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme, defaultLightTheme: defaultLightTheme.value === '' ? null : defaultLightTheme.value,
defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme, defaultDarkTheme: defaultDarkTheme.value === '' ? null : defaultDarkTheme.value,
infoImageUrl, infoImageUrl: infoImageUrl.value,
notFoundImageUrl, notFoundImageUrl: notFoundImageUrl.value,
serverErrorImageUrl, serverErrorImageUrl: serverErrorImageUrl.value,
manifestJsonOverride: manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride)), manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.branding, title: i18n.ts.branding,

View File

@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
import FormSuspense from '@/components/form/suspense.vue'; import FormSuspense from '@/components/form/suspense.vue';
import MkKeyValue from '@/components/MkKeyValue.vue'; import MkKeyValue from '@/components/MkKeyValue.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -29,9 +29,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)); const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.database, title: i18n.ts.database,

View File

@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
@ -78,23 +78,23 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
let enableEmail: boolean = $ref(false); const enableEmail = ref<boolean>(false);
let email: any = $ref(null); const email = ref<any>(null);
let smtpSecure: boolean = $ref(false); const smtpSecure = ref<boolean>(false);
let smtpHost: string = $ref(''); const smtpHost = ref<string>('');
let smtpPort: number = $ref(0); const smtpPort = ref<number>(0);
let smtpUser: string = $ref(''); const smtpUser = ref<string>('');
let smtpPass: string = $ref(''); const smtpPass = ref<string>('');
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
enableEmail = meta.enableEmail; enableEmail.value = meta.enableEmail;
email = meta.email; email.value = meta.email;
smtpSecure = meta.smtpSecure; smtpSecure.value = meta.smtpSecure;
smtpHost = meta.smtpHost; smtpHost.value = meta.smtpHost;
smtpPort = meta.smtpPort; smtpPort.value = meta.smtpPort;
smtpUser = meta.smtpUser; smtpUser.value = meta.smtpUser;
smtpPass = meta.smtpPass; smtpPass.value = meta.smtpPass;
} }
async function testEmail() { async function testEmail() {
@ -115,19 +115,19 @@ async function testEmail() {
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
enableEmail, enableEmail: enableEmail.value,
email, email: email.value,
smtpSecure, smtpSecure: smtpSecure.value,
smtpHost, smtpHost: smtpHost.value,
smtpPort, smtpPort: smtpPort.value,
smtpUser, smtpUser: smtpUser.value,
smtpPass, smtpPass: smtpPass.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.emailServer, title: i18n.ts.emailServer,

View File

@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@ -46,27 +46,27 @@ import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
let deeplAuthKey: string = $ref(''); const deeplAuthKey = ref<string>('');
let deeplIsPro: boolean = $ref(false); const deeplIsPro = ref<boolean>(false);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
deeplAuthKey = meta.deeplAuthKey; deeplAuthKey.value = meta.deeplAuthKey;
deeplIsPro = meta.deeplIsPro; deeplIsPro.value = meta.deeplIsPro;
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
deeplAuthKey, deeplAuthKey: deeplAuthKey.value,
deeplIsPro, deeplIsPro: deeplIsPro.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.externalServices, title: i18n.ts.externalServices,

View File

@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
@ -68,24 +68,24 @@ import FormSplit from '@/components/form/split.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
let host = $ref(''); const host = ref('');
let state = $ref('federating'); const state = ref('federating');
let sort = $ref('+pubSub'); const sort = ref('+pubSub');
const pagination = { const pagination = {
endpoint: 'federation/instances' as const, endpoint: 'federation/instances' as const,
limit: 10, limit: 10,
offsetMode: true, offsetMode: true,
params: computed(() => ({ params: computed(() => ({
sort: sort, sort: sort.value,
host: host !== '' ? host : null, host: host.value !== '' ? host.value : null,
...( ...(
state === 'federating' ? { federating: true } : state.value === 'federating' ? { federating: true } :
state === 'subscribing' ? { subscribing: true } : state.value === 'subscribing' ? { subscribing: true } :
state === 'publishing' ? { publishing: true } : state.value === 'publishing' ? { publishing: true } :
state === 'suspended' ? { suspended: true } : state.value === 'suspended' ? { suspended: true } :
state === 'blocked' ? { blocked: true } : state.value === 'blocked' ? { blocked: true } :
state === 'silenced' ? { silenced: true } : state.value === 'silenced' ? { silenced: true } :
state === 'notResponding' ? { notResponding: true } : state.value === 'notResponding' ? { notResponding: true } :
{}), {}),
})), })),
}; };
@ -98,9 +98,9 @@ function getStatus(instance) {
return 'Alive'; return 'Alive';
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({ definePageMetadata(computed(() => ({
title: i18n.ts.federation, title: i18n.ts.federation,

View File

@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
@ -45,19 +45,19 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
let origin = $ref('local'); const origin = ref('local');
let type = $ref(null); const type = ref(null);
let searchHost = $ref(''); const searchHost = ref('');
let userId = $ref(''); const userId = ref('');
let viewMode = $ref('grid'); const viewMode = ref('grid');
const pagination = { const pagination = {
endpoint: 'admin/drive/files' as const, endpoint: 'admin/drive/files' as const,
limit: 10, limit: 10,
params: computed(() => ({ params: computed(() => ({
type: (type && type !== '') ? type : null, type: (type.value && type.value !== '') ? type.value : null,
userId: (userId && userId !== '') ? userId : null, userId: (userId.value && userId.value !== '') ? userId.value : null,
origin: origin, origin: origin.value,
hostname: (searchHost && searchHost !== '') ? searchHost : null, hostname: (searchHost.value && searchHost.value !== '') ? searchHost.value : null,
})), })),
}; };
@ -95,7 +95,7 @@ async function find() {
}); });
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
text: i18n.ts.lookup, text: i18n.ts.lookup,
icon: 'ti ti-search', icon: 'ti ti-search',
handler: find, handler: find,
@ -105,7 +105,7 @@ const headerActions = $computed(() => [{
handler: clear, handler: clear,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({ definePageMetadata(computed(() => ({
title: i18n.ts.files, title: i18n.ts.files,

Some files were not shown because too many files have changed in this diff Show More