fix types
This commit is contained in:
parent
1a6ba246f2
commit
cee3a13f51
16 changed files with 194 additions and 191 deletions
6
packages/client/@types/window.d.ts
vendored
Normal file
6
packages/client/@types/window.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
__misskey_input_ref__?: HTMLInputElement | null;
|
||||
}
|
||||
}
|
||||
export type {};
|
|
@ -216,25 +216,32 @@ interface Input {
|
|||
| "paragraph";
|
||||
placeholder?: string | null;
|
||||
autocomplete?: string;
|
||||
default: string | number | null;
|
||||
default?: string | number | null;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
interface Select {
|
||||
items: {
|
||||
value: string;
|
||||
text: string;
|
||||
}[];
|
||||
groupedItems: {
|
||||
label: string;
|
||||
items: {
|
||||
value: string;
|
||||
text: string;
|
||||
}[];
|
||||
}[];
|
||||
default: string | null;
|
||||
}
|
||||
type Select = {
|
||||
default?: string | null;
|
||||
} & (
|
||||
| {
|
||||
items: {
|
||||
value: string;
|
||||
text: string;
|
||||
}[];
|
||||
groupedItems?: undefined;
|
||||
}
|
||||
| {
|
||||
items?: undefined;
|
||||
groupedItems: {
|
||||
label: string;
|
||||
items: {
|
||||
value: string;
|
||||
text: string;
|
||||
}[];
|
||||
}[];
|
||||
}
|
||||
);
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
|
|
@ -254,8 +254,7 @@ const isActive = ref();
|
|||
watch(
|
||||
() => props.items,
|
||||
() => {
|
||||
// FIXME: what's this?
|
||||
const items: (MenuItem | MenuPending)[] = [...props.items].filter(
|
||||
const items: (MenuItem | MenuPending)[] = props.items.filter(
|
||||
(item) => item !== undefined,
|
||||
);
|
||||
|
||||
|
|
|
@ -181,10 +181,10 @@ function adjustTweetHeight(message: any) {
|
|||
if (height) tweetHeight.value = height;
|
||||
}
|
||||
|
||||
(window as any).addEventListener("message", adjustTweetHeight);
|
||||
window.addEventListener("message", adjustTweetHeight);
|
||||
|
||||
onUnmounted(() => {
|
||||
(window as any).removeEventListener("message", adjustTweetHeight);
|
||||
window.removeEventListener("message", adjustTweetHeight);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
]"
|
||||
>
|
||||
<i
|
||||
v-if="success"
|
||||
v-if="unref(success)"
|
||||
:class="[$style.icon, $style.success, iconify('ph-check')]"
|
||||
></i>
|
||||
<MkLoading
|
||||
|
@ -29,15 +29,15 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef, watch } from "vue";
|
||||
import { MaybeRef, shallowRef, watch, unref } from "vue";
|
||||
import MkModal from "@/components/MkModal.vue";
|
||||
import iconify from "@/scripts/icon";
|
||||
|
||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
|
||||
const props = defineProps<{
|
||||
success: boolean;
|
||||
showing: boolean;
|
||||
success: MaybeRef<boolean>;
|
||||
showing: MaybeRef<boolean>;
|
||||
text?: string;
|
||||
}>();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
|
||||
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
import { type entities, api as firefishApi } from "firefish-js";
|
||||
import { type entities, api as firefishApi, type Endpoints } from "firefish-js";
|
||||
import insertTextAtCursor from "insert-text-at-cursor";
|
||||
import type { Component, Ref } from "vue";
|
||||
import { defineAsyncComponent, markRaw, ref } from "vue";
|
||||
|
@ -13,7 +13,7 @@ import MkWaitingDialog from "@/components/MkWaitingDialog.vue";
|
|||
import { apiUrl, url } from "@/config";
|
||||
import { me } from "@/me";
|
||||
import type { MenuItem } from "@/types/menu";
|
||||
import type { FormItemType, GetFormResultType } from "@/types/form";
|
||||
import type { Form, GetFormResultType } from "@/types/form";
|
||||
|
||||
export const pendingApiRequestsCount = ref(0);
|
||||
|
||||
|
@ -52,7 +52,7 @@ export const api = ((
|
|||
if (res.status === 200) {
|
||||
resolve(body);
|
||||
} else if (res.status === 204) {
|
||||
resolve();
|
||||
resolve(undefined);
|
||||
} else {
|
||||
reject(body.error);
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export const api = ((
|
|||
|
||||
export const apiGet = ((
|
||||
endpoint: string,
|
||||
data: Record<string, any> = {},
|
||||
data: URLSearchParams | string | string[][] | Record<string, string> = {},
|
||||
token?: string | null | undefined,
|
||||
) => {
|
||||
pendingApiRequestsCount.value++;
|
||||
|
@ -97,7 +97,7 @@ export const apiGet = ((
|
|||
if (res.status === 200) {
|
||||
resolve(body);
|
||||
} else if (res.status === 204) {
|
||||
resolve();
|
||||
resolve(undefined);
|
||||
} else {
|
||||
reject(body.error);
|
||||
}
|
||||
|
@ -111,15 +111,15 @@ export const apiGet = ((
|
|||
}) as typeof apiClient.request;
|
||||
|
||||
export const apiWithDialog = ((
|
||||
endpoint: string,
|
||||
data: Record<string, any> = {},
|
||||
endpoint: keyof Endpoints,
|
||||
data: Record<string, unknown> = {},
|
||||
token?: string | null | undefined,
|
||||
) => {
|
||||
const promise = api(endpoint, data, token);
|
||||
promiseDialog(promise, null, (err) => {
|
||||
alert({
|
||||
type: "error",
|
||||
text: err.message + "\n" + (err as any).id,
|
||||
text: `${err.message}\n${err.id}`,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -129,7 +129,7 @@ export const apiWithDialog = ((
|
|||
export function promiseDialog<T>(
|
||||
promise: Promise<T>,
|
||||
onSuccess?: ((res: T) => void) | null,
|
||||
onFailure?: ((err: Error) => void) | null,
|
||||
onFailure?: ((err: firefishApi.APIError) => void) | null,
|
||||
text?: string,
|
||||
): Promise<T> {
|
||||
const showing = ref(true);
|
||||
|
@ -294,7 +294,7 @@ export function alert(props: {
|
|||
text?: string | null;
|
||||
isPlaintext?: boolean;
|
||||
}): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
if (props.text == null && props.type === "error") {
|
||||
props.text = i18n.ts.somethingHappened;
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ export function confirm(props: {
|
|||
cancelText?: string;
|
||||
isPlaintext?: boolean;
|
||||
}): Promise<{ canceled: boolean }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
MkDialog,
|
||||
{
|
||||
|
@ -342,7 +342,7 @@ export function yesno(props: {
|
|||
text?: string | null;
|
||||
isPlaintext?: boolean;
|
||||
}): Promise<{ canceled: boolean }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDialog.vue"),
|
||||
|
@ -374,13 +374,13 @@ export function inputText(props: {
|
|||
minLength?: number;
|
||||
maxLength?: number;
|
||||
}): Promise<
|
||||
| { canceled: true; result: undefined }
|
||||
| { canceled: true; result?: undefined }
|
||||
| {
|
||||
canceled: false;
|
||||
result: string;
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
MkDialog,
|
||||
{
|
||||
|
@ -397,7 +397,14 @@ export function inputText(props: {
|
|||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result || { canceled: true });
|
||||
if (result.canceled) {
|
||||
resolve({ canceled: true });
|
||||
} else {
|
||||
resolve({
|
||||
canceled: false,
|
||||
result: String(result.result),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
|
@ -411,13 +418,13 @@ export function inputParagraph(props: {
|
|||
placeholder?: string | null;
|
||||
default?: string | null;
|
||||
}): Promise<
|
||||
| { canceled: true; result: undefined }
|
||||
| { canceled: true; result?: undefined }
|
||||
| {
|
||||
canceled: false;
|
||||
result: string;
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDialog.vue"),
|
||||
|
@ -435,7 +442,14 @@ export function inputParagraph(props: {
|
|||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result || { canceled: true });
|
||||
if (result.canceled) {
|
||||
resolve({ canceled: true });
|
||||
} else {
|
||||
resolve({
|
||||
canceled: false,
|
||||
result: String(result.result),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
|
@ -450,13 +464,13 @@ export function inputNumber(props: {
|
|||
default?: number | null;
|
||||
autocomplete?: string;
|
||||
}): Promise<
|
||||
| { canceled: true; result: undefined }
|
||||
| { canceled: true; result?: undefined }
|
||||
| {
|
||||
canceled: false;
|
||||
result: number;
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDialog.vue"),
|
||||
|
@ -475,7 +489,14 @@ export function inputNumber(props: {
|
|||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result || { canceled: true });
|
||||
if (result.canceled) {
|
||||
resolve({ canceled: true });
|
||||
} else {
|
||||
resolve({
|
||||
canceled: false,
|
||||
result: Number(result.result),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
|
@ -487,15 +508,16 @@ export function inputDate(props: {
|
|||
title?: string | null;
|
||||
text?: string | null;
|
||||
placeholder?: string | null;
|
||||
default?: Date | null;
|
||||
default?: Date | string | null;
|
||||
}): Promise<
|
||||
| { canceled: true; result: undefined }
|
||||
| { canceled: true; result?: undefined }
|
||||
| {
|
||||
canceled: false;
|
||||
result: Date;
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
props.default ??= new Date();
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
MkDialog,
|
||||
{
|
||||
|
@ -504,7 +526,10 @@ export function inputDate(props: {
|
|||
input: {
|
||||
type: "date",
|
||||
placeholder: props.placeholder,
|
||||
default: props.default,
|
||||
default:
|
||||
props.default instanceof Date
|
||||
? props.default.toISOString().slice(0, 10)
|
||||
: props.default,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -512,7 +537,7 @@ export function inputDate(props: {
|
|||
resolve(
|
||||
result
|
||||
? {
|
||||
result: new Date(result.result),
|
||||
result: new Date(result.result as string | number),
|
||||
canceled: false,
|
||||
}
|
||||
: { canceled: true },
|
||||
|
@ -524,7 +549,7 @@ export function inputDate(props: {
|
|||
});
|
||||
}
|
||||
|
||||
export function select<C = any>(
|
||||
export function select<C extends string>(
|
||||
props: {
|
||||
title?: string | null;
|
||||
text?: string | null;
|
||||
|
@ -535,8 +560,10 @@ export function select<C = any>(
|
|||
value: C;
|
||||
text: string;
|
||||
}[];
|
||||
groupedItems?: undefined;
|
||||
}
|
||||
| {
|
||||
items?: undefined;
|
||||
groupedItems: {
|
||||
label: string;
|
||||
items: {
|
||||
|
@ -547,27 +574,35 @@ export function select<C = any>(
|
|||
}
|
||||
),
|
||||
): Promise<
|
||||
| { canceled: true; result: undefined }
|
||||
| { canceled: true; result?: undefined }
|
||||
| {
|
||||
canceled: false;
|
||||
result: C;
|
||||
}
|
||||
> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
MkDialog,
|
||||
{
|
||||
title: props.title,
|
||||
text: props.text,
|
||||
select: {
|
||||
items: props.items,
|
||||
groupedItems: props.groupedItems,
|
||||
default: props.default,
|
||||
},
|
||||
select: props.items
|
||||
? {
|
||||
items: props.items,
|
||||
default: props.default,
|
||||
}
|
||||
: {
|
||||
groupedItems: props.groupedItems,
|
||||
default: props.default,
|
||||
},
|
||||
},
|
||||
{
|
||||
done: (result) => {
|
||||
resolve(result || { canceled: true });
|
||||
if (result.canceled) {
|
||||
resolve({ canceled: true });
|
||||
} else {
|
||||
resolve(result as never);
|
||||
}
|
||||
},
|
||||
},
|
||||
"closed",
|
||||
|
@ -576,7 +611,7 @@ export function select<C = any>(
|
|||
}
|
||||
|
||||
export function success(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const showing = ref(true);
|
||||
window.setTimeout(() => {
|
||||
showing.value = false;
|
||||
|
@ -596,7 +631,7 @@ export function success(): Promise<void> {
|
|||
}
|
||||
|
||||
export function waiting(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const showing = ref(true);
|
||||
popup(
|
||||
MkWaitingDialog,
|
||||
|
@ -612,14 +647,9 @@ export function waiting(): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
export function form<T extends Record<string, FormItemType>>(
|
||||
title: string,
|
||||
form: T,
|
||||
) {
|
||||
export function form<T extends Form>(title: string, form: T) {
|
||||
return new Promise<{
|
||||
result?: {
|
||||
[K in keyof T]: GetFormResultType<T[K]["type"]>;
|
||||
};
|
||||
result?: GetFormResultType<T>;
|
||||
canceled?: true;
|
||||
}>((resolve, _reject) => {
|
||||
popup(
|
||||
|
@ -639,8 +669,8 @@ export function form<T extends Record<string, FormItemType>>(
|
|||
});
|
||||
}
|
||||
|
||||
export async function selectUser() {
|
||||
return new Promise<entities.UserDetailed>((resolve, reject) => {
|
||||
export function selectUser() {
|
||||
return new Promise<entities.UserDetailed>((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkUserSelectDialog.vue"),
|
||||
|
@ -658,8 +688,8 @@ export async function selectUser() {
|
|||
});
|
||||
}
|
||||
|
||||
export async function selectLocalUser() {
|
||||
return new Promise((resolve, reject) => {
|
||||
export function selectLocalUser() {
|
||||
return new Promise<entities.UserDetailed>((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkUserSelectLocalDialog.vue"),
|
||||
|
@ -677,8 +707,8 @@ export async function selectLocalUser() {
|
|||
});
|
||||
}
|
||||
|
||||
export async function selectInstance(): Promise<entities.Instance> {
|
||||
return new Promise((resolve, reject) => {
|
||||
export function selectInstance(): Promise<entities.Instance> {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkInstanceSelectDialog.vue"),
|
||||
|
@ -696,8 +726,10 @@ export async function selectInstance(): Promise<entities.Instance> {
|
|||
});
|
||||
}
|
||||
|
||||
export async function selectDriveFile(multiple: boolean) {
|
||||
return new Promise((resolve, reject) => {
|
||||
export function selectDriveFile<Multiple extends boolean>(multiple: Multiple) {
|
||||
return new Promise<
|
||||
Multiple extends true ? entities.DriveFile[] : entities.DriveFile
|
||||
>((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDriveSelectDialog.vue"),
|
||||
|
@ -711,7 +743,7 @@ export async function selectDriveFile(multiple: boolean) {
|
|||
{
|
||||
done: (files) => {
|
||||
if (files) {
|
||||
resolve(multiple ? files : files[0]);
|
||||
resolve((multiple ? files : files[0]) as never);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -721,7 +753,7 @@ export async function selectDriveFile(multiple: boolean) {
|
|||
}
|
||||
|
||||
export async function selectDriveFolder(multiple: boolean) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkDriveSelectDialog.vue"),
|
||||
|
@ -745,7 +777,7 @@ export async function selectDriveFolder(multiple: boolean) {
|
|||
}
|
||||
|
||||
export async function pickEmoji(src: HTMLElement | null, opts) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkEmojiPickerDialog.vue"),
|
||||
|
@ -772,7 +804,7 @@ export async function cropImage(
|
|||
aspectRatio: number;
|
||||
},
|
||||
): Promise<entities.DriveFile> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkCropperDialog.vue"),
|
||||
|
@ -795,13 +827,13 @@ export async function cropImage(
|
|||
|
||||
type AwaitType<T> = T extends Promise<infer U>
|
||||
? U
|
||||
: T extends (...args: any[]) => Promise<infer V>
|
||||
: T extends (...args: unknown[]) => Promise<infer V>
|
||||
? V
|
||||
: T;
|
||||
let openingEmojiPicker: AwaitType<ReturnType<typeof popup>> | null = null;
|
||||
let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null;
|
||||
let activeTextarea: HTMLTextAreaElement | HTMLInputElement;
|
||||
export async function openEmojiPicker(
|
||||
src?: HTMLElement,
|
||||
src: HTMLElement | undefined,
|
||||
opts,
|
||||
initialTextarea: typeof activeTextarea,
|
||||
) {
|
||||
|
@ -809,7 +841,7 @@ export async function openEmojiPicker(
|
|||
|
||||
activeTextarea = initialTextarea;
|
||||
|
||||
const textareas = document.querySelectorAll("textarea, input");
|
||||
const textareas = document.querySelectorAll<HTMLTextAreaElement | HTMLInputElement>("textarea, input");
|
||||
for (const textarea of Array.from(textareas)) {
|
||||
textarea.addEventListener("focus", () => {
|
||||
activeTextarea = textarea;
|
||||
|
@ -821,7 +853,7 @@ export async function openEmojiPicker(
|
|||
for (const node of Array.from(record.addedNodes).filter(
|
||||
(node) => node instanceof HTMLElement,
|
||||
) as HTMLElement[]) {
|
||||
const textareas = node.querySelectorAll("textarea, input");
|
||||
const textareas = node.querySelectorAll<HTMLTextAreaElement | HTMLInputElement>("textarea, input");
|
||||
for (const textarea of Array.from(textareas).filter(
|
||||
(textarea) => textarea.dataset.preventEmojiInsert == null,
|
||||
)) {
|
||||
|
@ -859,7 +891,7 @@ export async function openEmojiPicker(
|
|||
insertTextAtCursor(activeTextarea, emoji);
|
||||
},
|
||||
closed: () => {
|
||||
openingEmojiPicker!.dispose();
|
||||
openingEmojiPicker?.dispose();
|
||||
openingEmojiPicker = null;
|
||||
observer.disconnect();
|
||||
},
|
||||
|
@ -910,8 +942,8 @@ export function contextMenu(
|
|||
ev: MouseEvent,
|
||||
) {
|
||||
ev.preventDefault();
|
||||
return new Promise((resolve, reject) => {
|
||||
let dispose;
|
||||
return new Promise<void>((resolve, _reject) => {
|
||||
let dispose: () => void;
|
||||
popup(
|
||||
defineAsyncComponent({
|
||||
loader: () => import("@/components/MkContextMenu.vue"),
|
||||
|
@ -938,7 +970,7 @@ export function post(
|
|||
props: InstanceType<typeof MkPostFormDialog>["$props"] = {},
|
||||
onClosed?: () => void,
|
||||
) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
return new Promise<void>((resolve, _reject) => {
|
||||
// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない
|
||||
// NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、
|
||||
// Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、
|
||||
|
@ -966,7 +998,7 @@ export const deckGlobalEvents = new EventEmitter();
|
|||
|
||||
/*
|
||||
export function checkExistence(fileData: ArrayBuffer): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const data = new FormData();
|
||||
data.append('md5', getMD5(fileData));
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ const props = withDefaults(
|
|||
const file = ref<any>(null);
|
||||
|
||||
async function choose() {
|
||||
os.selectDriveFile(false).then((fileResponse: any) => {
|
||||
os.selectDriveFile(false).then((fileResponse) => {
|
||||
file.value = fileResponse;
|
||||
props.value.fileId = fileResponse.id;
|
||||
props.value.fileId = fileResponse?.id;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -299,12 +299,12 @@ function loadFile(): void {
|
|||
});
|
||||
|
||||
// 一応廃棄
|
||||
(window as any).__misskey_input_ref__ = null;
|
||||
window.__misskey_input_ref__ = null;
|
||||
};
|
||||
|
||||
// https://qiita.com/fukasawah/items/b9dc732d95d99551013d
|
||||
// iOS Safari で正常に動かす為のおまじない
|
||||
(window as any).__misskey_input_ref__ = input;
|
||||
window.__misskey_input_ref__ = input;
|
||||
|
||||
input.click();
|
||||
}
|
||||
|
|
|
@ -56,8 +56,8 @@ async function timetravel() {
|
|||
title: i18n.ts.date,
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
tlEl.value.timetravel(date);
|
||||
// FIXME:
|
||||
tlEl.value!.timetravel(date);
|
||||
}
|
||||
|
||||
const headerActions = computed(() =>
|
||||
|
|
|
@ -1,72 +1,11 @@
|
|||
export type FormItem =
|
||||
| {
|
||||
label?: string;
|
||||
type: "string";
|
||||
default: string | null;
|
||||
hidden?: boolean;
|
||||
multiline?: boolean;
|
||||
}
|
||||
| {
|
||||
label?: string;
|
||||
type: "number";
|
||||
default: number | null;
|
||||
hidden?: boolean;
|
||||
step?: number;
|
||||
}
|
||||
| {
|
||||
label?: string;
|
||||
type: "boolean";
|
||||
default: boolean | null;
|
||||
hidden?: boolean;
|
||||
}
|
||||
| {
|
||||
label?: string;
|
||||
type: "enum";
|
||||
default: string | null;
|
||||
hidden?: boolean;
|
||||
enum: string[];
|
||||
}
|
||||
| {
|
||||
label?: string;
|
||||
type: "radio";
|
||||
default: unknown | null;
|
||||
hidden?: boolean;
|
||||
options: {
|
||||
label: string;
|
||||
value: unknown;
|
||||
}[];
|
||||
}
|
||||
| {
|
||||
label?: string;
|
||||
type: "object";
|
||||
default: Record<string, unknown> | null;
|
||||
hidden: true;
|
||||
}
|
||||
| {
|
||||
label?: string;
|
||||
type: "array";
|
||||
default: unknown[] | null;
|
||||
hidden: true;
|
||||
};
|
||||
// TODO: replace this file with @/types/form.ts
|
||||
|
||||
import type { FormItemType, GetFormItemResultType } from "@/types/form";
|
||||
|
||||
export type FormItem = FormItemType;
|
||||
|
||||
export type Form = Record<string, FormItem>;
|
||||
|
||||
type GetItemType<Item extends FormItem> = Item["type"] extends "string"
|
||||
? string
|
||||
: Item["type"] extends "number"
|
||||
? number
|
||||
: Item["type"] extends "boolean"
|
||||
? boolean
|
||||
: Item["type"] extends "radio"
|
||||
? unknown
|
||||
: Item["type"] extends "enum"
|
||||
? string
|
||||
: Item["type"] extends "array"
|
||||
? unknown[]
|
||||
: Item["type"] extends "object"
|
||||
? Record<string, unknown>
|
||||
: never;
|
||||
|
||||
export type GetFormResultType<F extends Form> = {
|
||||
[P in keyof F]: GetItemType<F[P]>;
|
||||
[P in keyof F]: NonNullable<GetFormItemResultType<F[P]["type"]>>;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ import icon from "@/scripts/icon";
|
|||
import { useRouter } from "@/router";
|
||||
import { notePage } from "@/filters/note";
|
||||
import type { NoteTranslation } from "@/types/note";
|
||||
import type { MenuItem } from "@/types/menu";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
@ -291,7 +292,7 @@ export function getNoteMenu(props: {
|
|||
props.translating.value = false;
|
||||
}
|
||||
|
||||
let menu;
|
||||
let menu: MenuItem[];
|
||||
if (isSignedIn) {
|
||||
const statePromise = os.api("notes/state", {
|
||||
noteId: appearNote.id,
|
||||
|
@ -396,7 +397,7 @@ export function getNoteMenu(props: {
|
|||
}
|
||||
: undefined,
|
||||
{
|
||||
type: "parent",
|
||||
type: "parent" as const,
|
||||
icon: `${icon("ph-share-network")}`,
|
||||
text: i18n.ts.share,
|
||||
children: [
|
||||
|
@ -499,7 +500,7 @@ export function getNoteMenu(props: {
|
|||
!isAppearAuthor ? null : undefined,
|
||||
!isAppearAuthor
|
||||
? {
|
||||
type: "parent",
|
||||
type: "parent" as const,
|
||||
icon: `${icon("ph-user")}`,
|
||||
text: i18n.ts.user,
|
||||
children: getUserMenu(appearNote.user),
|
||||
|
|
|
@ -9,12 +9,14 @@ import icon from "@/scripts/icon";
|
|||
|
||||
const stream = useStream();
|
||||
|
||||
function select(
|
||||
src: any,
|
||||
function select<Multiple extends boolean>(
|
||||
src: HTMLElement | null | undefined,
|
||||
label: string | null,
|
||||
multiple: boolean,
|
||||
): Promise<entities.DriveFile | entities.DriveFile[]> {
|
||||
return new Promise((res, rej) => {
|
||||
multiple: Multiple,
|
||||
) {
|
||||
return new Promise<
|
||||
Multiple extends true ? entities.DriveFile[] : entities.DriveFile
|
||||
>((res, rej) => {
|
||||
const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
|
||||
|
||||
const chooseFileFromPc = () => {
|
||||
|
@ -22,6 +24,9 @@ function select(
|
|||
input.type = "file";
|
||||
input.multiple = multiple;
|
||||
input.onchange = () => {
|
||||
if (input.files === null) {
|
||||
return;
|
||||
}
|
||||
const promises = Array.from(input.files).map((file) =>
|
||||
uploadFile(
|
||||
file,
|
||||
|
@ -33,19 +38,19 @@ function select(
|
|||
|
||||
Promise.all(promises)
|
||||
.then((driveFiles) => {
|
||||
res(multiple ? driveFiles : driveFiles[0]);
|
||||
res((multiple ? driveFiles : driveFiles[0]) as never);
|
||||
})
|
||||
.catch((err) => {
|
||||
// アップロードのエラーは uploadFile 内でハンドリングされているためアラートダイアログを出したりはしてはいけない
|
||||
});
|
||||
|
||||
// 一応廃棄
|
||||
(window as any).__misskey_input_ref__ = null;
|
||||
window.__misskey_input_ref__ = null;
|
||||
};
|
||||
|
||||
// https://qiita.com/fukasawah/items/b9dc732d95d99551013d
|
||||
// iOS Safari で正常に動かす為のおまじない
|
||||
(window as any).__misskey_input_ref__ = input;
|
||||
window.__misskey_input_ref__ = input;
|
||||
|
||||
input.click();
|
||||
};
|
||||
|
@ -69,7 +74,7 @@ function select(
|
|||
const connection = stream.useChannel("main");
|
||||
connection.on("urlUploadFinished", (urlResponse) => {
|
||||
if (urlResponse.marker === marker) {
|
||||
res(multiple ? [urlResponse.file] : urlResponse.file);
|
||||
res((multiple ? [urlResponse.file] : urlResponse.file) as never);
|
||||
connection.dispose();
|
||||
}
|
||||
});
|
||||
|
@ -122,15 +127,15 @@ function select(
|
|||
}
|
||||
|
||||
export function selectFile(
|
||||
src: any,
|
||||
src: HTMLElement | null | undefined,
|
||||
label: string | null = null,
|
||||
): Promise<entities.DriveFile> {
|
||||
return select(src, label, false) as Promise<entities.DriveFile>;
|
||||
) {
|
||||
return select(src, label, false);
|
||||
}
|
||||
|
||||
export function selectFiles(
|
||||
src: any,
|
||||
src: HTMLElement | null | undefined,
|
||||
label: string | null = null,
|
||||
): Promise<entities.DriveFile[]> {
|
||||
return select(src, label, true) as Promise<entities.DriveFile[]>;
|
||||
) {
|
||||
return select(src, label, true);
|
||||
}
|
||||
|
|
|
@ -54,18 +54,18 @@ export type FormItemSwitch = BaseFormItem & {
|
|||
};
|
||||
export type FormItemSelect = BaseFormItem & {
|
||||
type: "enum";
|
||||
default?: string | null;
|
||||
default?: string | number | symbol | null;
|
||||
enum: {
|
||||
value: string | number | symbol | undefined;
|
||||
value: string | number | symbol;
|
||||
label: string;
|
||||
}[];
|
||||
};
|
||||
export type FormItemRadios = BaseFormItem & {
|
||||
type: "radio";
|
||||
default?: string | number | symbol | undefined | null;
|
||||
default?: string | number | symbol | null;
|
||||
options: {
|
||||
label: string;
|
||||
value: string | number | symbol | undefined;
|
||||
value: string | number | symbol;
|
||||
}[];
|
||||
};
|
||||
export type FormItemRange = BaseFormItem & {
|
||||
|
@ -114,10 +114,25 @@ export type FormItemInput = FormItemInputArray[number];
|
|||
|
||||
export type FormItemType = FormItemTypeArray[number];
|
||||
|
||||
export type GetFormItemByType<T extends FormItemType["type"], F extends FormItemType = FormItemType> = F extends {type: T} ? F : never;
|
||||
export type Form = Record<string, FormItemType>;
|
||||
|
||||
export type GetFormItemByType<
|
||||
T extends FormItemType["type"],
|
||||
F extends FormItemType = FormItemType,
|
||||
> = F extends { type: T } ? F : never;
|
||||
|
||||
type NonUndefindAble<T> = T extends undefined ? never : T;
|
||||
type NonNullAble<T> = T extends null ? never : T;
|
||||
|
||||
export type GetFormResultType<T extends FormItemType["type"], I extends FormItemType = GetFormItemByType<T>> = NonUndefindAble<
|
||||
export type GetFormItemResultType<
|
||||
T extends FormItemType["type"],
|
||||
I extends FormItemType = GetFormItemByType<T>,
|
||||
> = NonUndefindAble<
|
||||
"__result_typedef" extends keyof I ? I["__result_typedef"] : I["default"]
|
||||
>
|
||||
>;
|
||||
|
||||
export type GetFormResultType<F extends Form> = {
|
||||
[K in keyof F]: F[K]["required"] extends false
|
||||
? GetFormItemResultType<F[K]["type"]>
|
||||
: NonNullAble<GetFormItemResultType<F[K]["type"]>>;
|
||||
};
|
||||
|
|
|
@ -23,9 +23,7 @@ export interface WidgetComponentExpose {
|
|||
configure: () => void;
|
||||
}
|
||||
|
||||
export const useWidgetPropsManager = <
|
||||
F extends Form & Record<string, { default: any }>,
|
||||
>(
|
||||
export const useWidgetPropsManager = <F extends Form>(
|
||||
name: string,
|
||||
propsDef: F,
|
||||
props: Readonly<WidgetComponentProps<GetFormResultType<F>>>,
|
||||
|
|
|
@ -122,6 +122,6 @@ export class APIClient {
|
|||
.catch(reject);
|
||||
});
|
||||
|
||||
return promise as any;
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import type {
|
|||
UserDetailed,
|
||||
UserGroup,
|
||||
UserList,
|
||||
UserLite,
|
||||
UserSorting,
|
||||
} from "./entities";
|
||||
|
||||
|
|
Loading…
Reference in a new issue