refactor: fix some type errors

This commit is contained in:
yukineko 2024-01-07 02:58:49 +09:00
parent 62aaaf9e92
commit 897b5d9bb1
No known key found for this signature in database
GPG Key ID: E5BACB72109B7B90
16 changed files with 99 additions and 66 deletions

View File

@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<!-- フォルダの中にはカスタム絵文字やフォルダがある -->
<section v-else v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
<header class="_acrylic" @click="shown = !shown">
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree?.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
</header>
<div v-if="shown" style="padding-left: 9px;">
<MkEmojiPickerSection
@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed, Ref } from 'vue';
import { CustomEmojiFolderTree, getEmojiName } from '@/scripts/emojilist.js';
import { i18n } from '../i18n.js';
import { i18n } from '@/i18n.js';
import { customEmojis } from '@/custom-emojis.js';
import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue';
@ -87,7 +87,7 @@ function computeButtonTitle(ev: MouseEvent): void {
elm.title = getEmojiName(emoji) ?? emoji;
}
function nestedChosen(emoji: any, ev?: MouseEvent) {
function nestedChosen(emoji: any, ev: MouseEvent) {
emit('chosen', emoji, ev);
}
</script>

View File

@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</section>
<div v-if="tab === 'index'" class="group index">
<section v-if="showPinned && pinned.length > 0">
<section v-if="showPinned && (pinned && pinned.length > 0)">
<div class="body">
<button
v-for="emoji in pinned"
@ -327,7 +327,7 @@ watch(q, () => {
});
function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean {
return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id)));
return ((emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction?.includes(r.id)))) ?? false;
}
function focus() {

View File

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:withOkButton="true"
:okButtonDisabled="false"
@ok="ok()"
@close="dialog.close()"
@close="dialog?.close()"
@closed="emit('closed')"
>
<template #header>{{ i18n.ts.describeFile }}</template>
@ -48,6 +48,6 @@ const caption = ref(props.default);
async function ok() {
emit('done', caption.value);
dialog.value.close();
dialog.value?.close();
}
</script>

View File

@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>
<MkPagination v-slot="{items}" :pagination="pagination" class="urempief" :class="{ grid: viewMode === 'grid' }">
<MkA
v-for="file in items"
v-for="file in (items as Misskey.entities.DriveFile[])"
:key="file.id"
v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Misskey.acct.toString(file.user) : 'system'}`"
:to="`/admin/file/${file.id}`"

View File

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="el" :class="$style.root">
<div ref="div" :class="$style.root">
<header :class="$style.header" class="_button" :style="{ background: bg }" @click="showBody = !showBody">
<div :class="$style.title"><div><slot name="header"></slot></div></div>
<div :class="$style.divider"></div>
@ -42,8 +42,8 @@ const props = withDefaults(defineProps<{
expanded: true,
});
const el = shallowRef<HTMLDivElement>();
const bg = ref<string | null>(null);
const div = shallowRef<HTMLDivElement>();
const bg = ref<string>();
const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded);
watch(showBody, () => {
@ -52,40 +52,44 @@ watch(showBody, () => {
}
});
function enter(el: Element) {
function enter(element: Element) {
const el = element as HTMLElement;
const elementHeight = el.getBoundingClientRect().height;
el.style.height = 0;
el.style.height = 'unset';
el.offsetHeight; // reflow
el.style.height = elementHeight + 'px';
}
function afterEnter(el: Element) {
el.style.height = null;
function afterEnter(element: Element) {
const el = element as HTMLElement;
el.style.height = 'unset';
}
function leave(el: Element) {
function leave(element: Element) {
const el = element as HTMLElement;
const elementHeight = el.getBoundingClientRect().height;
el.style.height = elementHeight + 'px';
el.offsetHeight; // reflow
el.style.height = 0;
el.style.height = '0';
}
function afterLeave(el: Element) {
el.style.height = null;
function afterLeave(element: Element) {
const el = element as HTMLElement;
el.style.height = 'unset';
}
onMounted(() => {
function getParentBg(el: HTMLElement | null): string {
function getParentBg(el?: HTMLElement | null): string {
if (el == null || el.tagName === 'BODY') return 'var(--bg)';
const bg = el.style.background || el.style.backgroundColor;
if (bg) {
return bg;
const background = el.style.background || el.style.backgroundColor;
if (background) {
return background;
} else {
return getParentBg(el.parentElement);
}
}
const rawBg = getParentBg(el.value);
const rawBg = getParentBg(div.value);
const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
_bg.setAlpha(0.85);
bg.value = _bg.toRgbString();

View File

@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
<div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null, overflow: maxHeight ? `auto` : null }" :aria-hidden="!opened">
<div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : undefined, overflow: maxHeight ? `auto` : undefined }" :aria-hidden="!opened">
<Transition
:enterActiveClass="defaultStore.state.animation ? $style.transition_toggle_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_toggle_leaveActive : ''"
@ -109,7 +109,7 @@ function toggle() {
onMounted(() => {
const computedStyle = getComputedStyle(document.documentElement);
const parentBg = getBgColor(rootEl.value.parentElement);
const parentBg = getBgColor(rootEl.value!.parentElement!);
const myBg = computedStyle.getPropertyValue('--panel');
bgSame.value = parentBg === myBg;
});

View File

@ -111,17 +111,19 @@ async function onClick() {
claimAchievement('following1');
if ($i.followingCount >= 10) {
claimAchievement('following10');
}
if ($i.followingCount >= 50) {
claimAchievement('following50');
}
if ($i.followingCount >= 100) {
claimAchievement('following100');
}
if ($i.followingCount >= 300) {
claimAchievement('following300');
if ($i) {
if ($i.followingCount >= 10) {
claimAchievement('following10');
}
if ($i.followingCount >= 50) {
claimAchievement('following50');
}
if ($i.followingCount >= 100) {
claimAchievement('following100');
}
if ($i.followingCount >= 300) {
claimAchievement('following300');
}
}
}
}

View File

@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
ref="dialog"
:width="370"
:height="400"
@close="dialog.close()"
@close="dialog?.close()"
@closed="emit('closed')"
>
<template #header>{{ i18n.ts.forgotPassword }}</template>
@ -66,6 +66,6 @@ async function onSubmit() {
email: email.value,
});
emit('done');
dialog.value.close();
dialog.value?.close();
}
</script>

View File

@ -40,11 +40,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
<MkSelect v-else-if="form[item].type === 'enum'" v-model="values[item]">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option>
<option v-for="option in form[item].enum" :key="option.value" :value="option.value">{{ option.label }}</option>
</MkSelect>
<MkRadios v-else-if="form[item].type === 'radio'" v-model="values[item]">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option>
<option v-for="option in form[item].options" :key="option.value" :value="option.value">{{ option.label }}</option>
</MkRadios>
<MkRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :textConverter="form[item].textConverter">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
@ -86,6 +86,7 @@ const emit = defineEmits<{
canceled?: boolean;
result?: any;
}): void;
(ev: 'closed'): void;
}>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
@ -99,13 +100,13 @@ function ok() {
emit('done', {
result: values,
});
dialog.value.close();
dialog.value?.close();
}
function cancel() {
emit('done', {
canceled: true,
});
dialog.value.close();
dialog.value?.close();
}
</script>

View File

@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only
leaveActiveClass: $style.transition_toggle_leaveActive,
leaveToClass: $style.transition_toggle_leaveTo,
}"
:src="post.files[0].thumbnailUrl"
:hash="post.files[0].blurhash"
:src="post.files?.[0]?.thumbnailUrl"
:hash="post.files?.[0]?.blurhash"
:forceBlurhash="!show"
/>
</Transition>

View File

@ -27,10 +27,10 @@ const props = defineProps<{
src: string;
}>();
const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = shallowRef<HTMLCanvasElement>(null);
const rootEl = shallowRef<HTMLDivElement | null>(null);
const chartEl = shallowRef<HTMLCanvasElement | null>(null);
const now = new Date();
let chartInstance: Chart = null;
let chartInstance: Chart | null = null;
const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip({
@ -38,6 +38,7 @@ const { handler: externalTooltipHandler } = useChartTooltip({
});
async function renderChart() {
if (!rootEl.value || !chartEl.value) return;
if (chartInstance) {
chartInstance.destroy();
}
@ -56,7 +57,7 @@ async function renderChart() {
return new Date(y, m, d - ago);
};
const format = (arr) => {
const format = (arr: number[]) => {
return arr.map((v, i) => {
const dt = getDate(i);
const iso = `${dt.getFullYear()}-${(dt.getMonth() + 1).toString().padStart(2, '0')}-${dt.getDate().toString().padStart(2, '0')}`;
@ -69,7 +70,7 @@ async function renderChart() {
});
};
let values;
let values: number[] = [];
if (props.src === 'active-users') {
const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
@ -106,20 +107,18 @@ async function renderChart() {
data: {
datasets: [{
label: 'Read & Write',
data: format(values),
pointRadius: 0,
data: format(values) as any,
borderWidth: 0,
borderJoinStyle: 'round',
borderRadius: 3,
backgroundColor(c) {
const value = c.dataset.data[c.dataIndex].v;
// @ts-expect-error TS(2339)
const value = c.dataset.data[c.dataIndex].v as number;
let a = (value - min) / max;
if (value !== 0) { // 0
a = Math.max(a, 0.05);
}
return alpha(color, a);
},
fill: true,
width(c) {
const a = c.chart.chartArea ?? {};
return (a.right - a.left) / weeks - marginEachCell;
@ -190,11 +189,13 @@ async function renderChart() {
enabled: false,
callbacks: {
title(context) {
const v = context[0].dataset.data[context[0].dataIndex];
return v.d;
// @ts-expect-error TS(2339)
return context[0].dataset.data[context[0].dataIndex].d;
},
label(context) {
const v = context.dataset.data[context.dataIndex];
// @ts-expect-error TS(2339)
return ['Active: ' + v.v];
},
},

View File

@ -138,7 +138,8 @@ function createDoughnut(chartEl, tooltip, data) {
},
},
onClick: (ev) => {
const hit = chartInstance.getElementsAtEventForMode(ev, 'nearest', { intersect: true }, false)[0];
if (!ev.native) return;
const hit = chartInstance.getElementsAtEventForMode(ev.native, 'nearest', { intersect: true }, false)[0];
if (hit && data[hit.index].onClick) {
data[hit.index].onClick();
}
@ -164,23 +165,46 @@ function createDoughnut(chartEl, tooltip, data) {
onMounted(() => {
misskeyApiGet('federation/stats', { limit: 30 }).then(fedStats => {
createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
type ChartData = {
name: string,
color: string | null,
value: number,
onClick?: () => void,
}[];
const subs: ChartData = fedStats.topSubInstances.map(x => ({
name: x.host,
color: x.themeColor,
value: x.followersCount,
onClick: () => {
os.pageWindow(`/instance-info/${x.host}`);
},
})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }]));
}));
createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
subs.push({
name: '(other)',
color: '#80808080',
value: fedStats.otherFollowersCount,
});
createDoughnut(subDoughnutEl.value, externalTooltipHandler1, subs);
const pubs: ChartData = fedStats.topPubInstances.map(x => ({
name: x.host,
color: x.themeColor,
value: x.followingCount,
onClick: () => {
os.pageWindow(`/instance-info/${x.host}`);
},
})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowingCount }]));
}));
pubs.push({
name: '(other)',
color: '#80808080',
value: fedStats.otherFollowingCount,
});
createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, pubs);
});
});
</script>

View File

@ -30,7 +30,7 @@ const instance = props.instance ?? {
themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content,
};
const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? '/favicon.ico');
const themeColor = instance.themeColor ?? '#777777';

View File

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModal ref="modal" v-slot="{ type, maxHeight }" :preferType="preferedModalType" :anchor="anchor" :transparentBg="true" :src="src" @click="modal.close()" @closed="emit('closed')">
<MkModal ref="modal" v-slot="{ type, maxHeight }" :preferType="preferedModalType" :anchor="anchor" :transparentBg="true" :src="src" @click="modal?.close()" @closed="emit('closed')">
<div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }">
<div class="main">
<template v-for="item in items" :key="item.text">
@ -63,7 +63,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k =>
}));
function close() {
modal.value.close();
modal.value?.close();
}
</script>

View File

@ -30,6 +30,7 @@ export default {
const contentEl = ref<HTMLElement>();
function calc() {
if (!contentEl.value) return;
const eachLength = contentEl.value.offsetWidth / props.repeat;
const factor = 3000;
const duration = props.duration / ((1 / eachLength) * factor);

View File

@ -21,7 +21,7 @@ import { host as hostRaw } from '@/config.js';
import { defaultStore } from '@/store.js';
defineProps<{
user: Misskey.entities.UserDetailed;
user: Misskey.entities.User;
detail?: boolean;
}>();