Merge branch 'develop' into patch-1

This commit is contained in:
MomentQYC 2024-10-31 16:56:10 +08:00 committed by GitHub
commit d8a29a5534
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 282 additions and 224 deletions

View file

@ -160,7 +160,7 @@ async function deleteAntenna() {
function addUser() { function addUser() {
os.selectUser({ includeSelf: true }).then(user => { os.selectUser({ includeSelf: true }).then(user => {
users.value = users.value.trim(); users.value = users.value.trim();
users.value += '\n@' + Misskey.acct.toString(user as any); users.value += '\n@' + Misskey.acct.toString(user);
users.value = users.value.trim(); users.value = users.value.trim();
}); });
} }

View file

@ -47,11 +47,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js'; import { miLocalStorage } from '@/local-storage.js';
const props = defineProps<{ const props = defineProps<{
channel: Record<string, any>; channel: Misskey.entities.Channel;
}>(); }>();
const getLastReadedAt = (): number | null => { const getLastReadedAt = (): number | null => {

View file

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:withOkButton="true" :withOkButton="true"
@close="cancel()" @close="cancel()"
@ok="ok()" @ok="ok()"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template #header>{{ i18n.ts.cropImage }}</template> <template #header>{{ i18n.ts.cropImage }}</template>
<template #default="{ width, height }"> <template #default="{ width, height }">

View file

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')"> <MkModalWindow ref="dialogEl" @close="cancel()" @closed="emit('closed')">
<template #header>:{{ emoji.name }}:</template> <template #header>:{{ emoji.name }}:</template>
<template #default> <template #default>
<MkSpacer> <MkSpacer>

View file

@ -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="okButtonDisabledReason" @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 != null" @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">
@ -98,7 +98,7 @@ const props = withDefaults(defineProps<{
text: string; text: string;
primary?: boolean, primary?: boolean,
danger?: boolean, danger?: boolean,
callback: (...args: any[]) => void; callback: (...args: unknown[]) => void;
}[]; }[];
showOkButton?: boolean; showOkButton?: boolean;
showCancelButton?: boolean; showCancelButton?: boolean;

View file

@ -157,7 +157,7 @@ const ilFilesObserver = new IntersectionObserver(
(entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles(), (entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles(),
); );
const sortModeSelect = ref('+createdAt'); const sortModeSelect = ref<NonNullable<Misskey.entities.DriveFilesRequest['sort']>>('+createdAt');
watch(folder, () => emit('cd', folder.value)); watch(folder, () => emit('cd', folder.value));
watch(sortModeSelect, () => { watch(sortModeSelect, () => {
@ -198,7 +198,7 @@ function onStreamDriveFolderDeleted(folderId: string) {
removeFolder(folderId); removeFolder(folderId);
} }
function onDragover(ev: DragEvent): any { function onDragover(ev: DragEvent) {
if (!ev.dataTransfer) return; if (!ev.dataTransfer) return;
// //
@ -243,7 +243,7 @@ function onDragleave() {
draghover.value = false; draghover.value = false;
} }
function onDrop(ev: DragEvent): any { function onDrop(ev: DragEvent) {
draghover.value = false; draghover.value = false;
if (!ev.dataTransfer) return; if (!ev.dataTransfer) return;
@ -332,7 +332,7 @@ function createFolder() {
title: i18n.ts.createFolder, title: i18n.ts.createFolder,
placeholder: i18n.ts.folderName, placeholder: i18n.ts.folderName,
}).then(({ canceled, result: name }) => { }).then(({ canceled, result: name }) => {
if (canceled) return; if (canceled || name == null) return;
misskeyApi('drive/folders/create', { misskeyApi('drive/folders/create', {
name: name, name: name,
parentId: folder.value ? folder.value.id : undefined, parentId: folder.value ? folder.value.id : undefined,

View file

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:scroll="false" :scroll="false"
:withOkButton="false" :withOkButton="false"
@close="cancel()" @close="cancel()"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template #header><i class="ti ti-code"></i> {{ i18n.ts._embedCodeGen.title }}</template> <template #header><i class="ti ti-code"></i> {{ i18n.ts._embedCodeGen.title }}</template>

View file

@ -90,7 +90,7 @@ function computeButtonTitle(ev: MouseEvent): void {
elm.title = getEmojiName(emoji); elm.title = getEmojiName(emoji);
} }
function nestedChosen(emoji: any, ev: MouseEvent) { function nestedChosen(emoji: string, ev: MouseEvent) {
emit('chosen', emoji, ev); emit('chosen', emoji, ev);
} }
</script> </script>

View file

@ -409,7 +409,7 @@ function computeButtonTitle(ev: MouseEvent): void {
elm.title = getEmojiName(emoji); elm.title = getEmojiName(emoji);
} }
function chosen(emoji: any, ev?: MouseEvent) { function chosen(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef, ev?: MouseEvent) {
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
if (el) { if (el) {
const rect = el.getBoundingClientRect(); const rect = el.getBoundingClientRect();
@ -426,7 +426,7 @@ function chosen(emoji: any, ev?: MouseEvent) {
// 使 // 使
if (!pinned.value?.includes(key)) { if (!pinned.value?.includes(key)) {
let recents = defaultStore.state.recentlyUsedEmojis; let recents = defaultStore.state.recentlyUsedEmojis;
recents = recents.filter((emoji: any) => emoji !== key); recents = recents.filter((emoji) => emoji !== key);
recents.unshift(key); recents.unshift(key);
defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32));
} }

View file

@ -73,7 +73,7 @@ export type Extension = {
author: string; author: string;
description?: string; description?: string;
permissions?: string[]; permissions?: string[];
config?: Record<string, any>; config?: Record<string, unknown>;
}; };
} | { } | {
type: 'theme'; type: 'theme';

View file

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@click="cancel()" @click="cancel()"
@ok="ok()" @ok="ok()"
@close="cancel()" @close="cancel()"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template #header> <template #header>
{{ title }} {{ title }}

View file

@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue'; import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs, InputHTMLAttributes } from 'vue';
import { debounce } from 'throttle-debounce'; import { debounce } from 'throttle-debounce';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { useInterval } from '@@/js/use-interval.js'; import { useInterval } from '@@/js/use-interval.js';
@ -53,7 +53,7 @@ import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js';
const props = defineProps<{ const props = defineProps<{
modelValue: string | number | null; modelValue: string | number | null;
type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; type?: InputHTMLAttributes['type'];
required?: boolean; required?: boolean;
readonly?: boolean; readonly?: boolean;
disabled?: boolean; disabled?: boolean;
@ -64,8 +64,8 @@ const props = defineProps<{
mfmAutocomplete?: boolean | SuggestionType[], mfmAutocomplete?: boolean | SuggestionType[],
autocapitalize?: string; autocapitalize?: string;
spellcheck?: boolean; spellcheck?: boolean;
inputmode?: 'none' | 'text' | 'search' | 'email' | 'url' | 'numeric' | 'tel' | 'decimal'; inputmode?: InputHTMLAttributes['inputmode'];
step?: any; step?: InputHTMLAttributes['step'];
datalist?: string[]; datalist?: string[];
min?: number; min?: number;
max?: number; max?: number;

View file

@ -227,7 +227,7 @@ const emit = defineEmits<{
}>(); }>();
const inTimeline = inject<boolean>('inTimeline', false); const inTimeline = inject<boolean>('inTimeline', false);
const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(false)); const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(true));
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);

View file

@ -53,7 +53,7 @@ const props = withDefaults(defineProps<{
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 = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as TypesMap);
function ok() { function ok() {
emit('done', { emit('done', {

View file

@ -39,7 +39,7 @@ import number from '@/filters/number.js';
import XValue from '@/components/MkObjectView.value.vue'; import XValue from '@/components/MkObjectView.value.vue';
const props = defineProps<{ const props = defineProps<{
value: any; value: unknown;
}>(); }>();
const collapsed = reactive({}); const collapsed = reactive({});
@ -50,19 +50,19 @@ if (isObject(props.value)) {
} }
} }
function isObject(v): boolean { function isObject(v: unknown): v is Record<PropertyKey, unknown> {
return typeof v === 'object' && !Array.isArray(v) && v !== null; return typeof v === 'object' && !Array.isArray(v) && v !== null;
} }
function isArray(v): boolean { function isArray(v: unknown): v is unknown[] {
return Array.isArray(v); return Array.isArray(v);
} }
function isEmpty(v): boolean { function isEmpty(v: unknown): v is Record<PropertyKey, never> | never[] {
return (isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0); return (isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0);
} }
function collapsable(v): boolean { function collapsable(v: unknown): boolean {
return (isObject(v) || isArray(v)) && !isEmpty(v); return (isObject(v) || isArray(v)) && !isEmpty(v);
} }
</script> </script>

View file

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:buttonsLeft="buttonsLeft" :buttonsLeft="buttonsLeft"
:buttonsRight="buttonsRight" :buttonsRight="buttonsRight"
:contextmenu="contextmenu" :contextmenu="contextmenu"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template #header> <template #header>
<template v-if="pageMetadata"> <template v-if="pageMetadata">
@ -30,17 +30,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue'; import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
import { url } from '@@/js/config.js';
import { getScrollContainer } from '@@/js/scroll.js';
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';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { url } from '@@/js/config.js';
import { useScrollPositionManager } from '@/nirax.js'; import { useScrollPositionManager } from '@/nirax.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import { openingWindowsCount } from '@/os.js'; import { openingWindowsCount } from '@/os.js';
import { claimAchievement } from '@/scripts/achievements.js'; import { claimAchievement } from '@/scripts/achievements.js';
import { getScrollContainer } from '@@/js/scroll.js';
import { useRouterFactory } from '@/router/supplier.js'; import { useRouterFactory } from '@/router/supplier.js';
import { mainRouter } from '@/router/main.js'; import { mainRouter } from '@/router/main.js';
@ -48,7 +48,7 @@ const props = defineProps<{
initialPath: string; initialPath: string;
}>(); }>();
defineEmits<{ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
@ -58,7 +58,7 @@ const windowRouter = routerFactory(props.initialPath);
const contents = shallowRef<HTMLElement | null>(null); const contents = shallowRef<HTMLElement | null>(null);
const pageMetadata = ref<null | PageMetadata>(null); const pageMetadata = ref<null | PageMetadata>(null);
const windowEl = shallowRef<InstanceType<typeof MkWindow>>(); const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
const history = ref<{ path: string; key: any; }[]>([{ const history = ref<{ path: string; key: string; }[]>([{
path: windowRouter.getCurrentPath(), path: windowRouter.getCurrentPath(),
key: windowRouter.getCurrentKey(), key: windowRouter.getCurrentKey(),
}]); }]);

View file

@ -19,7 +19,7 @@ defineProps<{
items: MenuItem[]; items: MenuItem[];
align?: 'center' | string; align?: 'center' | string;
width?: number; width?: number;
src?: any; src?: HTMLElement | null;
returnFocusTo?: HTMLElement | null; returnFocusTo?: HTMLElement | null;
}>(); }>();

View file

@ -129,25 +129,13 @@ import { miLocalStorage } from '@/local-storage.js';
import { claimAchievement } from '@/scripts/achievements.js'; import { claimAchievement } from '@/scripts/achievements.js';
import { emojiPicker } from '@/scripts/emoji-picker.js'; import { emojiPicker } from '@/scripts/emoji-picker.js';
import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js'; import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js';
import type { PostFormProps } from '@/types/post-form.js';
const $i = signinRequired(); const $i = signinRequired();
const modal = inject('modal'); const modal = inject('modal');
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<PostFormProps & {
reply?: Misskey.entities.Note;
renote?: Misskey.entities.Note;
channel?: Misskey.entities.Channel; // TODO
mention?: Misskey.entities.User;
specified?: Misskey.entities.UserDetailed;
initialText?: string;
initialCw?: string;
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
initialFiles?: Misskey.entities.DriveFile[];
initialLocalOnly?: boolean;
initialVisibleUsers?: Misskey.entities.UserDetailed[];
initialNote?: Misskey.entities.Note;
instant?: boolean;
fixed?: boolean; fixed?: boolean;
autofocus?: boolean; autofocus?: boolean;
freezeAfterPosted?: boolean; freezeAfterPosted?: boolean;
@ -955,8 +943,8 @@ function showActions(ev: MouseEvent) {
action.handler({ action.handler({
text: text.value, text: text.value,
cw: cw.value, cw: cw.value,
}, (key, value: any) => { }, (key, value) => {
if (typeof key !== 'string') return; if (typeof key !== 'string' || typeof value !== 'string') return;
if (key === 'text') { text.value = value; } if (key === 'text') { text.value = value; }
if (key === 'cw') { useCw.value = value !== null; cw.value = value; } if (key === 'cw') { useCw.value = value !== null; cw.value = value; }
}); });

View file

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div v-show="props.modelValue.length != 0" :class="$style.root"> <div v-show="props.modelValue.length != 0" :class="$style.root">
<Sortable :modelValue="props.modelValue" :class="$style.files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)"> <Sortable :modelValue="props.modelValue" :class="$style.files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)">
<template #item="{element}"> <template #item="{ element }">
<div <div
:class="$style.file" :class="$style.file"
role="button" role="button"
@ -38,14 +38,14 @@ import type { MenuItem } from '@/types/menu.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
const props = defineProps<{ const props = defineProps<{
modelValue: any[]; modelValue: Misskey.entities.DriveFile[];
detachMediaFn?: (id: string) => void; detachMediaFn?: (id: string) => void;
}>(); }>();
const mock = inject<boolean>('mock', false); const mock = inject<boolean>('mock', false);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'update:modelValue', value: any[]): void; (ev: 'update:modelValue', value: Misskey.entities.DriveFile[]): void;
(ev: 'detach', id: string): void; (ev: 'detach', id: string): void;
(ev: 'changeSensitive', file: Misskey.entities.DriveFile, isSensitive: boolean): void; (ev: 'changeSensitive', file: Misskey.entities.DriveFile, isSensitive: boolean): void;
(ev: 'changeName', file: Misskey.entities.DriveFile, newName: string): void; (ev: 'changeName', file: Misskey.entities.DriveFile, newName: string): void;
@ -113,7 +113,7 @@ async function rename(file) {
}); });
} }
async function describe(file) { async function describe(file: Misskey.entities.DriveFile) {
if (mock) return; if (mock) return;
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {

View file

@ -11,23 +11,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue'; import { shallowRef } from 'vue';
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';
import type { PostFormProps } from '@/types/post-form.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<PostFormProps & {
reply?: Misskey.entities.Note;
renote?: Misskey.entities.Note;
channel?: any; // TODO
mention?: Misskey.entities.User;
specified?: Misskey.entities.UserDetailed;
initialText?: string;
initialCw?: string;
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
initialFiles?: Misskey.entities.DriveFile[];
initialLocalOnly?: boolean;
initialVisibleUsers?: Misskey.entities.UserDetailed[];
initialNote?: Misskey.entities.Note;
instant?: boolean; instant?: boolean;
fixed?: boolean; fixed?: boolean;
autofocus?: boolean; autofocus?: boolean;

View file

@ -24,17 +24,17 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup generic="T extends unknown">
import { computed } from 'vue'; import { computed } from 'vue';
const props = defineProps<{ const props = defineProps<{
modelValue: any; modelValue: T;
value: any; value: T;
disabled?: boolean; disabled?: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'update:modelValue', value: any): void; (ev: 'update:modelValue', value: T): void;
}>(); }>();
const checked = computed(() => props.modelValue === props.value); const checked = computed(() => props.modelValue === props.value);

View file

@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { } from 'vue';
import * as Misskey from 'misskey-js';
import { getEmojiName } from '@@/js/emojilist.js'; import { getEmojiName } from '@@/js/emojilist.js';
import MkTooltip from './MkTooltip.vue'; import MkTooltip from './MkTooltip.vue';
import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue';
@ -30,7 +31,7 @@ import MkReactionIcon from '@/components/MkReactionIcon.vue';
defineProps<{ defineProps<{
showing: boolean; showing: boolean;
reaction: string; reaction: string;
users: any[]; // TODO users: Misskey.entities.UserLite[];
count: number; count: number;
targetElement: HTMLElement; targetElement: HTMLElement;
}>(); }>();

View file

@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:width="500" :width="500"
:height="600" :height="600"
@close="onClose" @close="onClose"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template #header>{{ i18n.ts.signup }}</template> <template #header>{{ i18n.ts.signup }}</template>

View file

@ -28,11 +28,38 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts">
import { } from 'vue'; export type SuperMenuDef = {
title?: string;
items: ({
type: 'a';
href: string;
target?: string;
icon?: string;
text: string;
danger?: boolean;
active?: boolean;
} | {
type: 'button';
icon?: string;
text: string;
danger?: boolean;
active?: boolean;
action: (ev: MouseEvent) => void;
} | {
type: 'link';
to: string;
icon?: string;
text: string;
danger?: boolean;
active?: boolean;
})[];
};
</script>
<script lang="ts" setup>
defineProps<{ defineProps<{
def: any[]; def: SuperMenuDef[];
grid?: boolean; grid?: boolean;
}>(); }>();
</script> </script>

View file

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:okButtonDisabled="false" :okButtonDisabled="false"
:canClose="false" :canClose="false"
@close="dialog?.close()" @close="dialog?.close()"
@closed="$emit('closed')" @closed="emit('closed')"
@ok="ok()" @ok="ok()"
> >
<template #header>{{ title || i18n.ts.generateAccessToken }}</template> <template #header>{{ title || i18n.ts.generateAccessToken }}</template>

View file

@ -180,7 +180,7 @@ window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLa
sensitive.value = info.sensitive ?? false; sensitive.value = info.sensitive ?? false;
}); });
function adjustTweetHeight(message: any) { function adjustTweetHeight(message: MessageEvent) {
if (message.origin !== 'https://platform.twitter.com') return; if (message.origin !== 'https://platform.twitter.com') return;
const embed = message.data?.['twttr.embed']; const embed = message.data?.['twttr.embed'];
if (embed?.method !== 'twttr.private.resize') return; if (embed?.method !== 'twttr.private.resize') return;
@ -193,14 +193,16 @@ function openPlayer(): void {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkYouTubePlayer.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkYouTubePlayer.vue')), {
url: requestUrl.href, url: requestUrl.href,
}, { }, {
// TODO closed: () => {
dispose();
},
}); });
} }
(window as any).addEventListener('message', adjustTweetHeight); window.addEventListener('message', adjustTweetHeight);
onUnmounted(() => { onUnmounted(() => {
(window as any).removeEventListener('message', adjustTweetHeight); window.removeEventListener('message', adjustTweetHeight);
}); });
</script> </script>

View file

@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
ref="dialog" ref="dialog"
:width="400" :width="400"
@close="dialog?.close()" @close="dialog?.close()"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template v-if="announcement" #header>:{{ announcement.title }}:</template> <template v-if="announcement" #header>:{{ announcement.title }}:</template>
<template v-else #header>New announcement</template> <template v-else #header>New announcement</template>
@ -62,9 +62,16 @@ import MkTextarea from '@/components/MkTextarea.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import MkRadios from '@/components/MkRadios.vue'; import MkRadios from '@/components/MkRadios.vue';
type AdminAnnouncementType = Misskey.entities.AdminAnnouncementsCreateRequest & { id: string; }
const props = defineProps<{ const props = defineProps<{
user: Misskey.entities.User, user: Misskey.entities.User,
announcement?: Misskey.entities.Announcement, announcement?: Required<AdminAnnouncementType>,
}>();
const emit = defineEmits<{
(ev: 'done', v: { deleted?: boolean; updated?: AdminAnnouncementType; created?: AdminAnnouncementType; }): void,
(ev: 'closed'): void
}>(); }>();
const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null); const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null);
@ -74,11 +81,6 @@ const icon = ref(props.announcement ? props.announcement.icon : 'info');
const display = ref(props.announcement ? props.announcement.display : 'dialog'); const display = ref(props.announcement ? props.announcement.display : 'dialog');
const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false); const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false);
const emit = defineEmits<{
(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
(ev: 'closed'): void
}>();
async function done() { async function done() {
const params = { const params = {
title: title.value, title: title.value,
@ -88,7 +90,7 @@ async function done() {
display: display.value, display: display.value,
needConfirmationToRead: needConfirmationToRead.value, needConfirmationToRead: needConfirmationToRead.value,
userId: props.user.id, userId: props.user.id,
}; } satisfies Misskey.entities.AdminAnnouncementsCreateRequest;
if (props.announcement) { if (props.announcement) {
await os.apiWithDialog('admin/announcements/update', { await os.apiWithDialog('admin/announcements/update', {

View file

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@click="cancel()" @click="cancel()"
@close="cancel()" @close="cancel()"
@ok="ok()" @ok="ok()"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template #header>{{ i18n.ts.selectUser }}</template> <template #header>{{ i18n.ts.selectUser }}</template>
<div> <div>

View file

@ -16,12 +16,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import * as Misskey from 'misskey-js';
import MkTooltip from './MkTooltip.vue'; import MkTooltip from './MkTooltip.vue';
defineProps<{ defineProps<{
showing: boolean; showing: boolean;
users: any[]; // TODO users: Misskey.entities.UserLite[];
count: number; count: number;
targetElement: HTMLElement; targetElement: HTMLElement;
}>(); }>();

View file

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.ts._widgets[widget] }}</option> <option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.ts._widgets[widget] }}</option>
</MkSelect> </MkSelect>
<MkButton inline primary data-cy-widget-add @click="addWidget"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> <MkButton inline primary data-cy-widget-add @click="addWidget"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
<MkButton inline @click="$emit('exit')">{{ i18n.ts.close }}</MkButton> <MkButton inline @click="emit('exit')">{{ i18n.ts.close }}</MkButton>
</header> </header>
<Sortable <Sortable
:modelValue="props.widgets" :modelValue="props.widgets"

View file

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:enterFromClass="defaultStore.state.animation ? $style.transition_window_enterFrom : ''" :enterFromClass="defaultStore.state.animation ? $style.transition_window_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_window_leaveTo : ''" :leaveToClass="defaultStore.state.animation ? $style.transition_window_leaveTo : ''"
appear appear
@afterLeave="$emit('closed')" @afterLeave="emit('closed')"
> >
<div v-if="showing" ref="rootEl" :class="[$style.root, { [$style.maximized]: maximized }]"> <div v-if="showing" ref="rootEl" :class="[$style.root, { [$style.maximized]: maximized }]">
<div :class="$style.body" class="_shadow" @mousedown="onBodyMousedown" @keydown="onKeydown"> <div :class="$style.body" class="_shadow" @mousedown="onBodyMousedown" @keydown="onKeydown">
@ -60,6 +60,13 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
type WindowButton = {
title: string;
icon: string;
onClick: () => void;
highlighted?: boolean;
};
const minHeight = 50; const minHeight = 50;
const minWidth = 250; const minWidth = 250;
@ -87,8 +94,8 @@ const props = withDefaults(defineProps<{
mini?: boolean; mini?: boolean;
front?: boolean; front?: boolean;
contextmenu?: MenuItem[] | null; contextmenu?: MenuItem[] | null;
buttonsLeft?: any[]; buttonsLeft?: WindowButton[];
buttonsRight?: any[]; buttonsRight?: WindowButton[];
}>(), { }>(), {
initialWidth: 400, initialWidth: 400,
initialHeight: null, initialHeight: null,

View file

@ -18,19 +18,19 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup generic="T extends unknown">
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
const props = defineProps<{ const props = defineProps<{
p: () => Promise<any>; p: () => Promise<T>;
}>(); }>();
const pending = ref(true); const pending = ref(true);
const resolved = ref(false); const resolved = ref(false);
const rejected = ref(false); const rejected = ref(false);
const result = ref<any>(null); const result = ref<T | null>(null);
const process = () => { const process = () => {
if (props.p == null) { if (props.p == null) {

View file

@ -467,8 +467,8 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
} }
default: { default: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // @ts-expect-error 存在しないASTタイプ
console.error('unrecognized ast type:', (token as any).type); console.error('unrecognized ast type:', token.type);
return []; return [];
} }

View file

@ -36,6 +36,8 @@ interface RouteDefWithRedirect extends RouteDefBase {
export type RouteDef = RouteDefWithComponent | RouteDefWithRedirect; export type RouteDef = RouteDefWithComponent | RouteDefWithRedirect;
export type RouterFlag = 'forcePage';
type ParsedPath = (string | { type ParsedPath = (string | {
name: string; name: string;
startsWith?: string; startsWith?: string;
@ -107,7 +109,7 @@ export interface IRouter extends EventEmitter<RouterEvent> {
current: Resolved; current: Resolved;
currentRef: ShallowRef<Resolved>; currentRef: ShallowRef<Resolved>;
currentRoute: ShallowRef<RouteDef>; currentRoute: ShallowRef<RouteDef>;
navHook: ((path: string, flag?: any) => boolean) | null; navHook: ((path: string, flag?: RouterFlag) => boolean) | null;
/** /**
* eventListenerの定義後に必ず呼び出すこと * eventListenerの定義後に必ず呼び出すこと
@ -116,11 +118,11 @@ export interface IRouter extends EventEmitter<RouterEvent> {
resolve(path: string): Resolved | null; resolve(path: string): Resolved | null;
getCurrentPath(): any; getCurrentPath(): string;
getCurrentKey(): string; getCurrentKey(): string;
push(path: string, flag?: any): void; push(path: string, flag?: RouterFlag): void;
replace(path: string, key?: string | null): void; replace(path: string, key?: string | null): void;
@ -197,7 +199,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
private currentKey = Date.now().toString(); private currentKey = Date.now().toString();
private redirectCount = 0; private redirectCount = 0;
public navHook: ((path: string, flag?: any) => boolean) | null = null; public navHook: ((path: string, flag?: RouterFlag) => boolean) | null = null;
constructor(routes: Router['routes'], currentPath: Router['currentPath'], isLoggedIn: boolean, notFoundPageComponent: Component) { constructor(routes: Router['routes'], currentPath: Router['currentPath'], isLoggedIn: boolean, notFoundPageComponent: Component) {
super(); super();
@ -404,7 +406,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
return this.currentKey; return this.currentKey;
} }
public push(path: string, flag?: any) { public push(path: string, flag?: RouterFlag) {
const beforePath = this.currentPath; const beforePath = this.currentPath;
if (path === beforePath) { if (path === beforePath) {
this.emit('same'); this.emit('same');

View file

@ -28,12 +28,13 @@ import { pleaseLogin } from '@/scripts/please-login.js';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js'; import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
import { focusParent } from '@/scripts/focus.js'; import { focusParent } from '@/scripts/focus.js';
import type { PostFormProps } from '@/types/post-form.js';
export const openingWindowsCount = ref(0); export const openingWindowsCount = ref(0);
export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req']>( export const apiWithDialog = (<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req']>(
endpoint: E, endpoint: E,
data: P = {} as any, data: P,
token?: string | null | undefined, token?: string | null | undefined,
customErrors?: Record<string, { title?: string; text: string; }>, customErrors?: Record<string, { title?: string; text: string; }>,
) => { ) => {
@ -94,7 +95,7 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
export function promiseDialog<T extends Promise<any>>( export function promiseDialog<T extends Promise<any>>(
promise: T, promise: T,
onSuccess?: ((res: any) => void) | null, onSuccess?: ((res: Awaited<T>) => void) | null,
onFailure?: ((err: Misskey.api.APIError) => void) | null, onFailure?: ((err: Misskey.api.APIError) => void) | null,
text?: string, text?: string,
): T { ): T {
@ -136,12 +137,12 @@ export function promiseDialog<T extends Promise<any>>(
} }
let popupIdCount = 0; let popupIdCount = 0;
export const popups = ref([]) as Ref<{ export const popups = ref<{
id: number; id: number;
component: Component; component: Component;
props: Record<string, any>; props: Record<string, any>;
events: Record<string, any>; events: Record<string, any>;
}[]>; }[]>([]);
const zIndexes = { const zIndexes = {
veryLow: 500000, veryLow: 500000,
@ -458,7 +459,7 @@ type SelectItem<C> = {
}; };
// default が指定されていたら result は null になり得ないことを保証する overload function // default が指定されていたら result は null になり得ないことを保証する overload function
export function select<C = any>(props: { export function select<C = unknown>(props: {
title?: string; title?: string;
text?: string; text?: string;
default: string; default: string;
@ -471,7 +472,7 @@ export function select<C = any>(props: {
} | { } | {
canceled: false; result: C; canceled: false; result: C;
}>; }>;
export function select<C = any>(props: { export function select<C = unknown>(props: {
title?: string; title?: string;
text?: string; text?: string;
default?: string | null; default?: string | null;
@ -484,7 +485,7 @@ export function select<C = any>(props: {
} | { } | {
canceled: false; result: C | null; canceled: false; result: C | null;
}>; }>;
export function select<C = any>(props: { export function select<C = unknown>(props: {
title?: string; title?: string;
text?: string; text?: string;
default?: string | null; default?: string | null;
@ -687,13 +688,13 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
})); }));
} }
export function post(props: Record<string, any> = {}): Promise<void> { export function post(props: PostFormProps = {}): Promise<void> {
pleaseLogin({ pleaseLogin({
openOnRemote: (props.initialText || props.initialNote ? { openOnRemote: (props.initialText || props.initialNote ? {
type: 'share', type: 'share',
params: { params: {
text: props.initialText ?? props.initialNote.text, text: props.initialText ?? props.initialNote?.text ?? '',
visibility: props.initialVisibility ?? props.initialNote?.visibility, visibility: props.initialVisibility ?? props.initialNote?.visibility ?? 'public',
localOnly: (props.initialLocalOnly || props.initialNote?.localOnly) ? '1' : '0', localOnly: (props.initialLocalOnly || props.initialNote?.localOnly) ? '1' : '0',
}, },
} : undefined), } : undefined),

View file

@ -99,19 +99,19 @@ async function addUser() {
const { canceled: canceled1, result: username } = await os.inputText({ const { canceled: canceled1, result: username } = await os.inputText({
title: i18n.ts.username, title: i18n.ts.username,
}); });
if (canceled1) return; if (canceled1 || username == null) return;
const { canceled: canceled2, result: password } = await os.inputText({ const { canceled: canceled2, result: password } = await os.inputText({
title: i18n.ts.password, title: i18n.ts.password,
type: 'password', type: 'password',
}); });
if (canceled2) return; if (canceled2 || password == null) return;
os.apiWithDialog('admin/accounts/create', { os.apiWithDialog('admin/accounts/create', {
username: username, username: username,
password: password, password: password,
}).then(res => { }).then(res => {
paginationComponent.value.reload(); paginationComponent.value?.reload();
}); });
} }

View file

@ -116,7 +116,7 @@ const selectAll = () => {
if (selectedEmojis.value.length > 0) { if (selectedEmojis.value.length > 0) {
selectedEmojis.value = []; selectedEmojis.value = [];
} else { } else {
selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id); selectedEmojis.value = Array.from(emojisPaginationComponent.value?.items.values(), item => item.id);
} }
}; };
@ -133,7 +133,7 @@ const add = async (ev: MouseEvent) => {
}, { }, {
done: result => { done: result => {
if (result.created) { if (result.created) {
emojisPaginationComponent.value.prepend(result.created); emojisPaginationComponent.value?.prepend(result.created);
} }
}, },
closed: () => dispose(), closed: () => dispose(),
@ -146,12 +146,12 @@ const edit = (emoji) => {
}, { }, {
done: result => { done: result => {
if (result.updated) { if (result.updated) {
emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ emojisPaginationComponent.value?.updateItem(result.updated.id, (oldEmoji) => ({
...oldEmoji, ...oldEmoji,
...result.updated, ...result.updated,
})); }));
} else if (result.deleted) { } else if (result.deleted) {
emojisPaginationComponent.value.removeItem(emoji.id); emojisPaginationComponent.value?.removeItem(emoji.id);
} }
}, },
closed: () => dispose(), closed: () => dispose(),
@ -226,7 +226,7 @@ const setCategoryBulk = async () => {
ids: selectedEmojis.value, ids: selectedEmojis.value,
category: result, category: result,
}); });
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value?.reload();
}; };
const setLicenseBulk = async () => { const setLicenseBulk = async () => {
@ -238,43 +238,43 @@ const setLicenseBulk = async () => {
ids: selectedEmojis.value, ids: selectedEmojis.value,
license: result, license: result,
}); });
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value?.reload();
}; };
const addTagBulk = async () => { const addTagBulk = async () => {
const { canceled, result } = await os.inputText({ const { canceled, result } = await os.inputText({
title: 'Tag', title: 'Tag',
}); });
if (canceled) return; if (canceled || result == null) return;
await os.apiWithDialog('admin/emoji/add-aliases-bulk', { await os.apiWithDialog('admin/emoji/add-aliases-bulk', {
ids: selectedEmojis.value, ids: selectedEmojis.value,
aliases: result.split(' '), aliases: result.split(' '),
}); });
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value?.reload();
}; };
const removeTagBulk = async () => { const removeTagBulk = async () => {
const { canceled, result } = await os.inputText({ const { canceled, result } = await os.inputText({
title: 'Tag', title: 'Tag',
}); });
if (canceled) return; if (canceled || result == null) return;
await os.apiWithDialog('admin/emoji/remove-aliases-bulk', { await os.apiWithDialog('admin/emoji/remove-aliases-bulk', {
ids: selectedEmojis.value, ids: selectedEmojis.value,
aliases: result.split(' '), aliases: result.split(' '),
}); });
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value?.reload();
}; };
const setTagBulk = async () => { const setTagBulk = async () => {
const { canceled, result } = await os.inputText({ const { canceled, result } = await os.inputText({
title: 'Tag', title: 'Tag',
}); });
if (canceled) return; if (canceled || result == null) return;
await os.apiWithDialog('admin/emoji/set-aliases-bulk', { await os.apiWithDialog('admin/emoji/set-aliases-bulk', {
ids: selectedEmojis.value, ids: selectedEmojis.value,
aliases: result.split(' '), aliases: result.split(' '),
}); });
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value?.reload();
}; };
const delBulk = async () => { const delBulk = async () => {
@ -286,7 +286,7 @@ const delBulk = async () => {
await os.apiWithDialog('admin/emoji/delete-bulk', { await os.apiWithDialog('admin/emoji/delete-bulk', {
ids: selectedEmojis.value, ids: selectedEmojis.value,
}); });
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value?.reload();
}; };
const headerActions = computed(() => [{ const headerActions = computed(() => [{

View file

@ -9,8 +9,8 @@ SPDX-License-Identifier: AGPL-3.0-only
:initialWidth="400" :initialWidth="400"
:initialHeight="500" :initialHeight="500"
:canResize="true" :canResize="true"
@close="windowEl.close()" @close="windowEl?.close()"
@closed="$emit('closed')" @closed="emit('closed')"
> >
<template v-if="emoji" #header>:{{ emoji.name }}:</template> <template v-if="emoji" #header>:{{ emoji.name }}:</template>
<template v-else #header>New emoji</template> <template v-else #header>New emoji</template>
@ -95,14 +95,19 @@ import { selectFile } from '@/scripts/select-file.js';
import MkRolePreview from '@/components/MkRolePreview.vue'; import MkRolePreview from '@/components/MkRolePreview.vue';
const props = defineProps<{ const props = defineProps<{
emoji?: any, emoji?: Misskey.entities.EmojiDetailed,
}>();
const emit = defineEmits<{
(ev: 'done', v: { deleted?: boolean; updated?: Misskey.entities.AdminEmojiUpdateRequest; created?: Misskey.entities.AdminEmojiUpdateRequest }): void,
(ev: 'closed'): void
}>(); }>();
const windowEl = ref<InstanceType<typeof MkWindow> | null>(null); const windowEl = ref<InstanceType<typeof MkWindow> | null>(null);
const name = ref<string>(props.emoji ? props.emoji.name : ''); const name = ref<string>(props.emoji ? props.emoji.name : '');
const category = ref<string>(props.emoji ? props.emoji.category : ''); const category = ref<string>(props.emoji?.category ? props.emoji.category : '');
const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : ''); const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : '');
const license = ref<string>(props.emoji ? (props.emoji.license ?? '') : ''); const license = ref<string>(props.emoji?.license ? props.emoji.license : '');
const isSensitive = ref(props.emoji ? props.emoji.isSensitive : false); const isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
const localOnly = ref(props.emoji ? props.emoji.localOnly : false); const localOnly = ref(props.emoji ? props.emoji.localOnly : false);
const roleIdsThatCanBeUsedThisEmojiAsReaction = ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); const roleIdsThatCanBeUsedThisEmojiAsReaction = ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
@ -115,12 +120,7 @@ watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null);
const emit = defineEmits<{ async function changeImage(ev: Event) {
(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
(ev: 'closed'): void
}>();
async function changeImage(ev) {
file.value = await selectFile(ev.currentTarget ?? ev.target, null); file.value = await selectFile(ev.currentTarget ?? ev.target, null);
const candidate = file.value.name.replace(/\.(.+)$/, ''); const candidate = file.value.name.replace(/\.(.+)$/, '');
if (candidate.match(/^[a-z0-9_]+$/)) { if (candidate.match(/^[a-z0-9_]+$/)) {
@ -140,7 +140,7 @@ async function addRole() {
rolesThatCanBeUsedThisEmojiAsReaction.value.push(role); rolesThatCanBeUsedThisEmojiAsReaction.value.push(role);
} }
async function removeRole(role, ev) { async function removeRole(role: Misskey.entities.RoleLite, ev: Event) {
rolesThatCanBeUsedThisEmojiAsReaction.value = rolesThatCanBeUsedThisEmojiAsReaction.value.filter(x => x.id !== role.id); rolesThatCanBeUsedThisEmojiAsReaction.value = rolesThatCanBeUsedThisEmojiAsReaction.value.filter(x => x.id !== role.id);
} }
@ -172,7 +172,7 @@ async function done() {
}, },
}); });
windowEl.value.close(); windowEl.value?.close();
} else { } else {
const created = await os.apiWithDialog('admin/emoji/add', params); const created = await os.apiWithDialog('admin/emoji/add', params);
@ -180,11 +180,12 @@ async function done() {
created: created, created: created,
}); });
windowEl.value.close(); windowEl.value?.close();
} }
} }
async function del() { async function del() {
if (!props.emoji) return;
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.tsx.removeAreYouSure({ x: name.value }), text: i18n.tsx.removeAreYouSure({ x: name.value }),
@ -197,7 +198,7 @@ async function del() {
emit('done', { emit('done', {
deleted: true, deleted: true,
}); });
windowEl.value.close(); windowEl.value?.close();
}); });
} }
</script> </script>

View file

@ -55,13 +55,13 @@ const pagination = {
function accept(user) { function accept(user) {
misskeyApi('following/requests/accept', { userId: user.id }).then(() => { misskeyApi('following/requests/accept', { userId: user.id }).then(() => {
paginationComponent.value.reload(); paginationComponent.value?.reload();
}); });
} }
function reject(user) { function reject(user) {
misskeyApi('following/requests/reject', { userId: user.id }).then(() => { misskeyApi('following/requests/reject', { userId: user.id }).then(() => {
paginationComponent.value.reload(); paginationComponent.value?.reload();
}); });
} }

View file

@ -40,7 +40,7 @@ function fetch() {
return; return;
} }
let promise: Promise<any>; let promise: Promise<unknown>;
if (uri.startsWith('https://')) { if (uri.startsWith('https://')) {
promise = misskeyApi('ap/show', { promise = misskeyApi('ap/show', {

View file

@ -77,15 +77,15 @@ async function create() {
clipsCache.delete(); clipsCache.delete();
pagingComponent.value.reload(); pagingComponent.value?.reload();
} }
function onClipCreated() { function onClipCreated() {
pagingComponent.value.reload(); pagingComponent.value?.reload();
} }
function onClipDeleted() { function onClipDeleted() {
pagingComponent.value.reload(); pagingComponent.value?.reload();
} }
const headerActions = computed(() => []); const headerActions = computed(() => []);

View file

@ -110,7 +110,7 @@ function addUser() {
listId: list.value.id, listId: list.value.id,
userId: user.id, userId: user.id,
}).then(() => { }).then(() => {
paginationEl.value.reload(); paginationEl.value?.reload();
}); });
}); });
} }
@ -126,7 +126,7 @@ async function removeUser(item, ev) {
listId: list.value.id, listId: list.value.id,
userId: item.userId, userId: item.userId,
}).then(() => { }).then(() => {
paginationEl.value.removeItem(item.id); paginationEl.value?.removeItem(item.id);
}); });
}, },
}], ev.currentTarget ?? ev.target); }], ev.currentTarget ?? ev.target);

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => emit('remove')">
<template #header><i class="ti ti-photo"></i> {{ i18n.ts._pages.blocks.image }}</template> <template #header><i class="ti ti-photo"></i> {{ i18n.ts._pages.blocks.image }}</template>
<template #func> <template #func>
<button @click="choose()"> <button @click="choose()">
@ -30,11 +30,12 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
const props = defineProps<{ const props = defineProps<{
modelValue: any modelValue: Misskey.entities.PageBlock & { type: 'image' };
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'update:modelValue', value: any): void; (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'image' }): void;
(ev: 'remove'): void;
}>(); }>();
const file = ref<Misskey.entities.DriveFile | null>(null); const file = ref<Misskey.entities.DriveFile | null>(null);

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => emit('remove')">
<template #header><i class="ti ti-note"></i> {{ i18n.ts._pages.blocks.note }}</template> <template #header><i class="ti ti-note"></i> {{ i18n.ts._pages.blocks.note }}</template>
<section style="padding: 16px;" class="_gaps_s"> <section style="padding: 16px;" class="_gaps_s">
@ -34,19 +34,24 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
const props = defineProps<{ const props = defineProps<{
modelValue: any modelValue: Misskey.entities.PageBlock & { type: 'note' };
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'update:modelValue', value: any): void; (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'note' }): void;
}>(); }>();
const id = ref<any>(props.modelValue.note); const id = ref(props.modelValue.note);
const note = ref<Misskey.entities.Note | null>(null); const note = ref<Misskey.entities.Note | null>(null);
watch(id, async () => { watch(id, async () => {
if (id.value && (id.value.startsWith('http://') || id.value.startsWith('https://'))) { if (id.value && (id.value.startsWith('http://') || id.value.startsWith('https://'))) {
id.value = (id.value.endsWith('/') ? id.value.slice(0, -1) : id.value).split('/').pop(); id.value = (id.value.endsWith('/') ? id.value.slice(0, -1) : id.value).split('/').pop() ?? null;
}
if (!id.value) {
note.value = null;
return;
} }
emit('update:modelValue', { emit('update:modelValue', {

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => emit('remove')">
<template #header><i class="ti ti-note"></i> {{ props.modelValue.title }}</template> <template #header><i class="ti ti-note"></i> {{ props.modelValue.title }}</template>
<template #func> <template #func>
<button class="_button" @click="rename()"> <button class="_button" @click="rename()">
@ -21,8 +21,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
/* eslint-disable vue/no-mutating-props */
import { defineAsyncComponent, inject, onMounted, watch, ref } from 'vue'; import { defineAsyncComponent, inject, onMounted, watch, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -33,14 +34,13 @@ import { getPageBlockList } from '@/pages/page-editor/common.js';
const XBlocks = defineAsyncComponent(() => import('../page-editor.blocks.vue')); const XBlocks = defineAsyncComponent(() => import('../page-editor.blocks.vue'));
const props = withDefaults(defineProps<{ const props = defineProps<{
modelValue: any, modelValue: Misskey.entities.PageBlock & { type: 'section'; },
}>(), { }>();
modelValue: {},
});
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'update:modelValue', value: any): void; (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'section' }): void;
(ev: 'remove'): void;
}>(); }>();
const children = ref(deepClone(props.modelValue.children ?? [])); const children = ref(deepClone(props.modelValue.children ?? []));

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => emit('remove')">
<template #header><i class="ti ti-align-left"></i> {{ i18n.ts._pages.blocks.text }}</template> <template #header><i class="ti ti-align-left"></i> {{ i18n.ts._pages.blocks.text }}</template>
<section> <section>
@ -15,18 +15,19 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
/* eslint-disable vue/no-mutating-props */
import { watch, ref, shallowRef, onMounted, onUnmounted } from 'vue'; import { watch, ref, shallowRef, onMounted, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { Autocomplete } from '@/scripts/autocomplete.js'; import { Autocomplete } from '@/scripts/autocomplete.js';
const props = defineProps<{ const props = defineProps<{
modelValue: any modelValue: Misskey.entities.PageBlock & { type: 'text' }
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'update:modelValue', value: any): void; (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'text' }): void;
}>(); }>();
let autocomplete: Autocomplete; let autocomplete: Autocomplete;

View file

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<Sortable :modelValue="modelValue" tag="div" itemKey="id" handle=".drag-handle" :group="{ name: 'blocks' }" :animation="150" :swapThreshold="0.5" @update:modelValue="v => $emit('update:modelValue', v)"> <Sortable :modelValue="modelValue" tag="div" itemKey="id" handle=".drag-handle" :group="{ name: 'blocks' }" :animation="150" :swapThreshold="0.5" @update:modelValue="v => emit('update:modelValue', v)">
<template #item="{element}"> <template #item="{element}">
<div :class="$style.item"> <div :class="$style.item">
<!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 --> <!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->

View file

@ -52,7 +52,7 @@ const props = defineProps<{
const scope = computed(() => props.path ? props.path.split('/') : []); const scope = computed(() => props.path ? props.path.split('/') : []);
const keys = ref<any>(null); const keys = ref<[string, string][]>([]);
function fetchKeys() { function fetchKeys() {
misskeyApi('i/registry/keys-with-type', { misskeyApi('i/registry/keys-with-type', {

View file

@ -132,7 +132,7 @@ const mapCategories = Array.from(new Set(Object.values(Reversi.maps).map(x => x.
const props = defineProps<{ const props = defineProps<{
game: Misskey.entities.ReversiGameDetailed; game: Misskey.entities.ReversiGameDetailed;
connection: Misskey.ChannelConnection; connection: Misskey.ChannelConnection<Misskey.Channels['reversiGame']>;
}>(); }>();
const shareWhenStart = defineModel<boolean>('shareWhenStart', { default: false }); const shareWhenStart = defineModel<boolean>('shareWhenStart', { default: false });
@ -217,14 +217,14 @@ function onChangeReadyStates(states) {
game.value.user2Ready = states.user2; game.value.user2Ready = states.user2;
} }
function updateSettings(key: keyof Misskey.entities.ReversiGameDetailed) { function updateSettings(key: typeof Misskey.reversiUpdateKeys[number]) {
props.connection.send('updateSettings', { props.connection.send('updateSettings', {
key: key, key: key,
value: game.value[key], value: game.value[key],
}); });
} }
function onUpdateSettings({ userId, key, value }: { userId: string; key: keyof Misskey.entities.ReversiGameDetailed; value: any; }) { function onUpdateSettings<K extends typeof Misskey.reversiUpdateKeys[number]>({ userId, key, value }: { userId: string; key: K; value: Misskey.entities.ReversiGameDetailed[K]; }) {
if (userId === $i.id) return; if (userId === $i.id) return;
if (game.value[key] === value) return; if (game.value[key] === value) return;
game.value[key] = value; game.value[key] = value;

View file

@ -76,7 +76,11 @@ import { claimAchievement } from '@/scripts/achievements.js';
const parser = new Parser(); const parser = new Parser();
let aiscript: Interpreter; let aiscript: Interpreter;
const code = ref(''); const code = ref('');
const logs = ref<any[]>([]); const logs = ref<{
id: number;
text: string;
print: boolean;
}[]>([]);
const root = ref<AsUiRoot>(); const root = ref<AsUiRoot>();
const components = ref<Ref<AsUiComponent>[]>([]); const components = ref<Ref<AsUiComponent>[]>([]);
const uiKey = ref(0); const uiKey = ref(0);

View file

@ -138,12 +138,13 @@ const token = ref<string | number | null>(null);
const backupCodes = ref<string[]>(); const backupCodes = ref<string[]>();
function cancel() { function cancel() {
dialog.value.close(); dialog.value?.close();
} }
async function tokenDone() { async function tokenDone() {
if (token.value == null) return;
const res = await os.apiWithDialog('i/2fa/done', { const res = await os.apiWithDialog('i/2fa/done', {
token: token.value.toString(), token: typeof token.value === 'string' ? token.value : token.value.toString(),
}); });
backupCodes.value = res.backupCodes; backupCodes.value = res.backupCodes;
@ -166,7 +167,7 @@ function downloadBackupCodes() {
} }
function allDone() { function allDone() {
dialog.value.close(); dialog.value?.close();
} }
</script> </script>

View file

@ -90,7 +90,7 @@ function createAccount() {
}); });
} }
async function switchAccount(account: any) { async function switchAccount(account: Misskey.entities.UserDetailed) {
const fetchedAccounts = await getAccounts(); const fetchedAccounts = await getAccounts();
const token = fetchedAccounts.find(x => x.id === account.id)!.token; const token = fetchedAccounts.find(x => x.id === account.id)!.token;
switchAccountWithToken(token); switchAccountWithToken(token);

View file

@ -55,6 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import * as Misskey from 'misskey-js';
import FormPagination from '@/components/MkPagination.vue'; import FormPagination from '@/components/MkPagination.vue';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -77,7 +78,7 @@ const pagination = {
function revoke(token) { function revoke(token) {
misskeyApi('i/revoke-token', { tokenId: token.id }).then(() => { misskeyApi('i/revoke-token', { tokenId: token.id }).then(() => {
list.value.reload(); list.value?.reload();
}); });
} }

View file

@ -110,7 +110,7 @@ const decorationsForPreview = computed(() => {
}); });
function cancel() { function cancel() {
dialog.value.close(); dialog.value?.close();
} }
async function update() { async function update() {
@ -120,7 +120,7 @@ async function update() {
offsetX: offsetX.value, offsetX: offsetX.value,
offsetY: offsetY.value, offsetY: offsetY.value,
}); });
dialog.value.close(); dialog.value?.close();
} }
async function attach() { async function attach() {
@ -130,12 +130,12 @@ async function attach() {
offsetX: offsetX.value, offsetX: offsetX.value,
offsetY: offsetY.value, offsetY: offsetY.value,
}); });
dialog.value.close(); dialog.value?.close();
} }
async function detach() { async function detach() {
emit('detach'); emit('detach');
dialog.value.close(); dialog.value?.close();
} }
</script> </script>

View file

@ -257,7 +257,7 @@ function parallaxLoop() {
} }
function parallax() { function parallax() {
const banner = bannerEl.value as any; const banner = bannerEl.value;
if (banner == null) return; if (banner == null) return;
const top = getScrollPosition(rootEl.value); const top = getScrollPosition(rootEl.value);

View file

@ -10,7 +10,7 @@ import { $i, iAmModerator } from '@/account.js';
import MkLoading from '@/pages/_loading_.vue'; import MkLoading from '@/pages/_loading_.vue';
import MkError from '@/pages/_error_.vue'; import MkError from '@/pages/_error_.vue';
export const page = (loader: AsyncComponentLoader<any>) => defineAsyncComponent({ export const page = (loader: AsyncComponentLoader) => defineAsyncComponent({
loader: loader, loader: loader,
loadingComponent: MkLoading, loadingComponent: MkLoading,
errorComponent: MkError, errorComponent: MkError,

View file

@ -4,7 +4,7 @@
*/ */
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import { IRouter, Resolved, RouteDef, RouterEvent } from '@/nirax.js'; import { IRouter, Resolved, RouteDef, RouterEvent, RouterFlag } from '@/nirax.js';
import type { App, ShallowRef } from 'vue'; import type { App, ShallowRef } from 'vue';
@ -79,7 +79,7 @@ class MainRouterProxy implements IRouter {
return this.supplier().currentRoute; return this.supplier().currentRoute;
} }
get navHook(): ((path: string, flag?: any) => boolean) | null { get navHook(): ((path: string, flag?: RouterFlag) => boolean) | null {
return this.supplier().navHook; return this.supplier().navHook;
} }
@ -91,11 +91,11 @@ class MainRouterProxy implements IRouter {
return this.supplier().getCurrentKey(); return this.supplier().getCurrentKey();
} }
getCurrentPath(): any { getCurrentPath(): string {
return this.supplier().getCurrentPath(); return this.supplier().getCurrentPath();
} }
push(path: string, flag?: any): void { push(path: string, flag?: RouterFlag): void {
this.supplier().push(path, flag); this.supplier().push(path, flag);
} }

View file

@ -2,8 +2,9 @@
* SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import * as Misskey from 'misskey-js';
export function checkWordMute(note: Record<string, any>, me: Record<string, any> | null | undefined, mutedWords: Array<string | string[]>): boolean { export function checkWordMute(note: Misskey.entities.Note, me: Misskey.entities.UserLite | null | undefined, mutedWords: Array<string | string[]>): boolean {
// 自分自身 // 自分自身
if (me && (note.userId === me.id)) return false; if (me && (note.userId === me.id)) return false;

View file

@ -15,7 +15,7 @@ type Hidden = boolean | ((v: any) => boolean);
export type FormItem = { export type FormItem = {
label?: string; label?: string;
type: 'string'; type: 'string';
default: string | null; default?: string | null;
description?: string; description?: string;
required?: boolean; required?: boolean;
hidden?: Hidden; hidden?: Hidden;
@ -24,7 +24,7 @@ export type FormItem = {
} | { } | {
label?: string; label?: string;
type: 'number'; type: 'number';
default: number | null; default?: number | null;
description?: string; description?: string;
required?: boolean; required?: boolean;
hidden?: Hidden; hidden?: Hidden;
@ -32,20 +32,20 @@ export type FormItem = {
} | { } | {
label?: string; label?: string;
type: 'boolean'; type: 'boolean';
default: boolean | null; default?: boolean | null;
description?: string; description?: string;
hidden?: Hidden; hidden?: Hidden;
} | { } | {
label?: string; label?: string;
type: 'enum'; type: 'enum';
default: string | null; default?: string | null;
required?: boolean; required?: boolean;
hidden?: Hidden; hidden?: Hidden;
enum: EnumItem[]; enum: EnumItem[];
} | { } | {
label?: string; label?: string;
type: 'radio'; type: 'radio';
default: unknown | null; default?: unknown | null;
required?: boolean; required?: boolean;
hidden?: Hidden; hidden?: Hidden;
options: { options: {
@ -55,7 +55,7 @@ export type FormItem = {
} | { } | {
label?: string; label?: string;
type: 'range'; type: 'range';
default: number | null; default?: number | null;
description?: string; description?: string;
required?: boolean; required?: boolean;
step?: number; step?: number;
@ -66,12 +66,12 @@ export type FormItem = {
} | { } | {
label?: string; label?: string;
type: 'object'; type: 'object';
default: Record<string, unknown> | null; default?: Record<string, unknown> | null;
hidden: Hidden; hidden: Hidden;
} | { } | {
label?: string; label?: string;
type: 'array'; type: 'array';
default: unknown[] | null; default?: unknown[] | null;
hidden: Hidden; hidden: Hidden;
} | { } | {
type: 'button'; type: 'button';

View file

@ -17,7 +17,7 @@ export function misskeyApi<
_ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT, _ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT,
>( >(
endpoint: E, endpoint: E,
data: P = {} as any, data: P & { i?: string | null; } = {} as any,
token?: string | null | undefined, token?: string | null | undefined,
signal?: AbortSignal, signal?: AbortSignal,
): Promise<_ResT> { ): Promise<_ResT> {
@ -30,8 +30,8 @@ export function misskeyApi<
const promise = new Promise<_ResT>((resolve, reject) => { const promise = new Promise<_ResT>((resolve, reject) => {
// Append a credential // Append a credential
if ($i) (data as any).i = $i.token; if ($i) data.i = $i.token;
if (token !== undefined) (data as any).i = token; if (token !== undefined) data.i = token;
// Send request // Send request
window.fetch(`${apiUrl}/${endpoint}`, { window.fetch(`${apiUrl}/${endpoint}`, {

View file

@ -80,7 +80,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> {
}); });
} }
function select(src: any, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> { function select(src: HTMLElement | EventTarget | null, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
return new Promise((res, rej) => { return new Promise((res, rej) => {
const keepOriginal = ref(defaultStore.state.keepOriginalUploading); const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
@ -107,10 +107,10 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Miss
}); });
} }
export function selectFile(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile> { export function selectFile(src: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile> {
return select(src, label, false).then(files => files[0]); return select(src, label, false).then(files => files[0]);
} }
export function selectFiles(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile[]> { export function selectFiles(src: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile[]> {
return select(src, label, true); return select(src, label, true);
} }

View file

@ -6,8 +6,9 @@
/** /**
* () * ()
*/ */
export function shuffle<T extends any[]>(array: T): T { export function shuffle<T extends unknown[]>(array: T): T {
let currentIndex = array.length, randomIndex; let currentIndex = array.length;
let randomIndex: number;
// While there remain elements to shuffle. // While there remain elements to shuffle.
while (currentIndex !== 0) { while (currentIndex !== 0) {

View file

@ -32,13 +32,13 @@ const mimeTypeMap = {
export function uploadFile( export function uploadFile(
file: File, file: File,
folder?: any, folder?: string | Misskey.entities.DriveFolder,
name?: string, name?: string,
keepOriginal: boolean = defaultStore.state.keepOriginalUploading, keepOriginal: boolean = defaultStore.state.keepOriginalUploading,
): Promise<Misskey.entities.DriveFile> { ): Promise<Misskey.entities.DriveFile> {
if ($i == null) throw new Error('Not logged in'); if ($i == null) throw new Error('Not logged in');
if (folder && typeof folder === 'object') folder = folder.id; const _folder = typeof folder === 'string' ? folder : folder?.id;
if (file.size > instance.maxFileSize) { if (file.size > instance.maxFileSize) {
alert({ alert({
@ -89,11 +89,11 @@ export function uploadFile(
} }
const formData = new FormData(); const formData = new FormData();
formData.append('i', $i.token); formData.append('i', $i!.token);
formData.append('force', 'true'); formData.append('force', 'true');
formData.append('file', resizedImage ?? file); formData.append('file', resizedImage ?? file);
formData.append('name', ctx.name); formData.append('name', ctx.name);
if (folder) formData.append('folderId', folder); if (_folder) formData.append('folderId', _folder);
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', apiUrl + '/drive/files/create', true); xhr.open('POST', apiUrl + '/drive/files/create', true);

View file

@ -11,6 +11,7 @@ import darkTheme from '@@/themes/d-green-lime.json5';
import { miLocalStorage } from './local-storage.js'; import { miLocalStorage } from './local-storage.js';
import type { SoundType } from '@/scripts/sound.js'; import type { SoundType } from '@/scripts/sound.js';
import { Storage } from '@/pizzax.js'; import { Storage } from '@/pizzax.js';
import type { Ast } from '@syuilo/aiscript';
interface PostFormAction { interface PostFormAction {
title: string, title: string,
@ -516,7 +517,7 @@ export type Plugin = {
token: string; token: string;
src: string | null; src: string | null;
version: string; version: string;
ast: any[]; ast: Ast.Node[];
author?: string; author?: string;
description?: string; description?: string;
permissions?: string[]; permissions?: string[];
@ -554,13 +555,13 @@ export class ColdDeviceStorage {
} }
public static getAll(): Partial<typeof this.default> { public static getAll(): Partial<typeof this.default> {
return (Object.keys(this.default) as (keyof typeof this.default)[]).reduce((acc, key) => { return (Object.keys(this.default) as (keyof typeof this.default)[]).reduce<Partial<typeof this.default>>((acc, key) => {
const value = localStorage.getItem(PREFIX + key); const value = localStorage.getItem(PREFIX + key);
if (value != null) { if (value != null) {
acc[key] = JSON.parse(value); acc[key] = JSON.parse(value);
} }
return acc; return acc;
}, {} as any); }, {});
} }
public static set<T extends keyof typeof ColdDeviceStorage.default>(key: T, value: typeof ColdDeviceStorage.default[T]): void { public static set<T extends keyof typeof ColdDeviceStorage.default>(key: T, value: typeof ColdDeviceStorage.default[T]): void {
@ -605,7 +606,7 @@ export class ColdDeviceStorage {
get: () => { get: () => {
return valueRef.value; return valueRef.value;
}, },
set: (value: unknown) => { set: (value: typeof ColdDeviceStorage.default[K]) => {
const val = value; const val = value;
ColdDeviceStorage.set(key, val); ColdDeviceStorage.set(key, val);
}, },

View file

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Misskey from 'misskey-js';
export interface PostFormProps {
reply?: Misskey.entities.Note;
renote?: Misskey.entities.Note;
channel?: Misskey.entities.Channel; // TODO
mention?: Misskey.entities.User;
specified?: Misskey.entities.UserDetailed;
initialText?: string;
initialCw?: string;
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
initialFiles?: Misskey.entities.DriveFile[];
initialLocalOnly?: boolean;
initialVisibleUsers?: Misskey.entities.UserDetailed[];
initialNote?: Misskey.entities.Note;
instant?: boolean;
};

View file

@ -68,10 +68,10 @@ const onDriveFileCreated = (file) => {
} }
}; };
const thumbnail = (image: any): string => { const thumbnail = (image: Misskey.entities.DriveFile): string => {
return defaultStore.state.disableShowingAnimatedImages return defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(image.url) ? getStaticImageUrl(image.url)
: image.thumbnailUrl; : image.thumbnailUrl ?? image.url;
}; };
misskeyApi('drive/stream', { misskeyApi('drive/stream', {