From 5994a1d61519e7d0b4bfef262b16a0549e0bd92f Mon Sep 17 00:00:00 2001
From: Lhcfl <Lhcfl@outlook.com>
Date: Wed, 24 Apr 2024 00:02:28 +0800
Subject: [PATCH] fix types

---
 .../client/src/scripts/collect-page-vars.ts   | 10 ++-
 .../client/src/scripts/copy-to-clipboard.ts   | 22 +++++-
 .../client/src/scripts/extract-mentions.ts    |  2 +-
 packages/client/src/scripts/extract-mfm.ts    |  3 +-
 .../src/scripts/extract-url-from-mfm.ts       |  2 +-
 packages/client/src/scripts/focus.ts          |  2 +
 .../client/src/scripts/gen-search-query.ts    | 38 ---------
 packages/client/src/scripts/get-note-menu.ts  |  5 +-
 packages/client/src/types/page.ts             | 78 +++++++++++++++++++
 packages/firefish-js/src/api.types.ts         | 20 ++++-
 packages/firefish-js/src/type-utils.ts        |  2 +
 11 files changed, 133 insertions(+), 51 deletions(-)
 delete mode 100644 packages/client/src/scripts/gen-search-query.ts
 create mode 100644 packages/client/src/types/page.ts

diff --git a/packages/client/src/scripts/collect-page-vars.ts b/packages/client/src/scripts/collect-page-vars.ts
index e7d4a6d0e6..d410981e9a 100644
--- a/packages/client/src/scripts/collect-page-vars.ts
+++ b/packages/client/src/scripts/collect-page-vars.ts
@@ -1,6 +1,8 @@
-export function collectPageVars(content) {
-	const pageVars = [];
-	const collect = (xs: any[]) => {
+import type { PageContent, PageVar } from "@/types/page";
+
+export function collectPageVars(content: PageContent[]) {
+	const pageVars: PageVar[] = [];
+	const collect = (xs: PageContent[]) => {
 		for (const x of xs) {
 			if (x.type === "textInput") {
 				pageVars.push({
@@ -24,7 +26,7 @@ export function collectPageVars(content) {
 				pageVars.push({
 					name: x.name,
 					type: "boolean",
-					value: x.default,
+					value: x.default!,
 				});
 			} else if (x.type === "counter") {
 				pageVars.push({
diff --git a/packages/client/src/scripts/copy-to-clipboard.ts b/packages/client/src/scripts/copy-to-clipboard.ts
index a4835d8e79..b989a1bc43 100644
--- a/packages/client/src/scripts/copy-to-clipboard.ts
+++ b/packages/client/src/scripts/copy-to-clipboard.ts
@@ -1,7 +1,7 @@
 /**
  * Clipboardに値をコピー(TODO: 文字列以外も対応)
  */
-export default (val) => {
+function obsoleteCopyToClipboard(val: string) {
 	// 空div 生成
 	const tmp = document.createElement("div");
 	// 選択用のタグ生成
@@ -21,7 +21,7 @@ export default (val) => {
 	// body に追加
 	document.body.appendChild(tmp);
 	// 要素を選択
-	document.getSelection().selectAllChildren(tmp);
+	document.getSelection()?.selectAllChildren(tmp);
 
 	// クリップボードにコピー
 	const result = document.execCommand("copy");
@@ -30,4 +30,20 @@ export default (val) => {
 	document.body.removeChild(tmp);
 
 	return result;
-};
+}
+
+export default async function (val?: string | null) {
+	if (val == null) return true;
+	const clipboardObj = window.navigator?.clipboard;
+	if (clipboardObj == null) {
+		// not supported
+		return obsoleteCopyToClipboard(val);
+	} else {
+		return new Promise<boolean>((res) => {
+			clipboardObj
+				.writeText(val)
+				.then(() => res(true))
+				.catch(() => res(obsoleteCopyToClipboard(val)));
+		});
+	}
+}
diff --git a/packages/client/src/scripts/extract-mentions.ts b/packages/client/src/scripts/extract-mentions.ts
index 259f78e576..2f238dc15e 100644
--- a/packages/client/src/scripts/extract-mentions.ts
+++ b/packages/client/src/scripts/extract-mentions.ts
@@ -6,7 +6,7 @@ export function extractMentions(
 	nodes: mfm.MfmNode[],
 ): mfm.MfmMention["props"][] {
 	// TODO: 重複を削除
-	const mentionNodes = mfm.extract(nodes, (node) => node.type === "mention");
+	const mentionNodes = mfm.extract(nodes, (node) => node.type === "mention") as mfm.MfmMention[];
 	const mentions = mentionNodes.map((x) => x.props);
 
 	return mentions;
diff --git a/packages/client/src/scripts/extract-mfm.ts b/packages/client/src/scripts/extract-mfm.ts
index b02557b341..e5dfb7029b 100644
--- a/packages/client/src/scripts/extract-mfm.ts
+++ b/packages/client/src/scripts/extract-mfm.ts
@@ -15,7 +15,8 @@ const animatedMfm = [
 export function extractMfmWithAnimation(nodes: mfm.MfmNode[]): string[] {
 	const mfmNodes = mfm.extract(nodes, (node) => {
 		return node.type === "fn" && animatedMfm.includes(node.props.name);
-	});
+	}) as mfm.MfmFn[];
+	// FIXME: mfm type error
 	const mfms = mfmNodes.map((x) => x.props.fn);
 
 	return mfms;
diff --git a/packages/client/src/scripts/extract-url-from-mfm.ts b/packages/client/src/scripts/extract-url-from-mfm.ts
index 0c580b6d32..e142640df3 100644
--- a/packages/client/src/scripts/extract-url-from-mfm.ts
+++ b/packages/client/src/scripts/extract-url-from-mfm.ts
@@ -14,7 +14,7 @@ export function extractUrlFromMfm(
 			node.type === "url" ||
 			(node.type === "link" && !(respectSilentFlag && node.props.silent))
 		);
-	});
+	}) as (mfm.MfmLink | mfm.MfmUrl)[];
 	const urls: string[] = unique(urlNodes.map((x) => x.props.url));
 
 	return urls.reduce((array, url) => {
diff --git a/packages/client/src/scripts/focus.ts b/packages/client/src/scripts/focus.ts
index 878132fbe8..d490a1045d 100644
--- a/packages/client/src/scripts/focus.ts
+++ b/packages/client/src/scripts/focus.ts
@@ -1,5 +1,6 @@
 export function focusPrev(el: Element | null, self = false, scroll = true) {
 	if (el == null) return;
+	// biome-ignore lint/style/noParameterAssign: assign it intentionally
 	if (!self) el = el.previousElementSibling;
 	if (el) {
 		if (el.hasAttribute("tabindex")) {
@@ -14,6 +15,7 @@ export function focusPrev(el: Element | null, self = false, scroll = true) {
 
 export function focusNext(el: Element | null, self = false, scroll = true) {
 	if (el == null) return;
+	// biome-ignore lint/style/noParameterAssign: assign it intentionally
 	if (!self) el = el.nextElementSibling;
 	if (el) {
 		if (el.hasAttribute("tabindex")) {
diff --git a/packages/client/src/scripts/gen-search-query.ts b/packages/client/src/scripts/gen-search-query.ts
deleted file mode 100644
index 21bc5df34e..0000000000
--- a/packages/client/src/scripts/gen-search-query.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { acct } from "firefish-js";
-import { host as localHost } from "@/config";
-
-export async function genSearchQuery(v: any, q: string) {
-	let host: string;
-	let userId: string;
-	if (q.split(" ").some((x) => x.startsWith("@"))) {
-		for (const at of q
-			.split(" ")
-			.filter((x) => x.startsWith("@"))
-			.map((x) => x.slice(1))) {
-			if (at.includes(".")) {
-				if (at === localHost || at === ".") {
-					host = null;
-				} else {
-					host = at;
-				}
-			} else {
-				const user = await v.os
-					.api("users/show", acct.parse(at))
-					.catch((x) => null);
-				if (user) {
-					userId = user.id;
-				} else {
-					// todo: show error
-				}
-			}
-		}
-	}
-	return {
-		query: q
-			.split(" ")
-			.filter((x) => !(x.startsWith("/") || x.startsWith("@")))
-			.join(" "),
-		host,
-		userId,
-	};
-}
diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts
index 1bc2941acc..9985bef26d 100644
--- a/packages/client/src/scripts/get-note-menu.ts
+++ b/packages/client/src/scripts/get-note-menu.ts
@@ -15,6 +15,7 @@ import { useRouter } from "@/router";
 import { notePage } from "@/filters/note";
 import type { NoteTranslation } from "@/types/note";
 import type { MenuItem } from "@/types/menu";
+import type { NoteDraft } from "@/types/post-form";
 
 const router = useRouter();
 
@@ -61,7 +62,7 @@ export function getNoteMenu(props: {
 			});
 
 			os.post({
-				initialNote: appearNote,
+				initialNote: appearNote as NoteDraft,
 				renote: appearNote.renote,
 				reply: appearNote.reply,
 				channel: appearNote.channel,
@@ -71,7 +72,7 @@ export function getNoteMenu(props: {
 
 	function edit(): void {
 		os.post({
-			initialNote: appearNote,
+			initialNote: appearNote as NoteDraft,
 			renote: appearNote.renote,
 			reply: appearNote.reply,
 			channel: appearNote.channel,
diff --git a/packages/client/src/types/page.ts b/packages/client/src/types/page.ts
new file mode 100644
index 0000000000..30750e104d
--- /dev/null
+++ b/packages/client/src/types/page.ts
@@ -0,0 +1,78 @@
+import type { TypeUtils } from "firefish-js";
+
+export type BasePageContent = {
+	name: string;
+};
+
+export type PageContentTextInput = BasePageContent & {
+	type: "textInput";
+	default: string;
+};
+
+export type PageContentTextareaInput = BasePageContent & {
+	type: "textareaInput";
+	default?: string;
+};
+
+export type PageContentNumberInput = BasePageContent & {
+	type: "numberInput";
+	default?: number;
+};
+
+export type PageContentSwitch = BasePageContent & {
+	type: "switch";
+	default?: boolean;
+};
+export type PageContentCounter = BasePageContent & {
+	type: "counter";
+	default?: number;
+};
+
+export type PageContentRadioButton = BasePageContent & {
+	type: "radioButton";
+	default?: string;
+};
+
+export type PageContentChildren =
+	| PageContentTextInput
+	| PageContentTextareaInput
+	| PageContentNumberInput
+	| PageContentSwitch
+	| PageContentCounter
+	| PageContentRadioButton;
+
+export type PageContentParent = {
+	type: "parent";
+	children: PageContentChildren[];
+};
+
+export type PageContent = PageContentParent | PageContentChildren;
+
+export type GetPageVar<T extends PageContentChildren> = {
+	name: string;
+	type: TypeUtils.NonUndefinedAble<T["default"]> extends string
+		? "string"
+		: TypeUtils.NonUndefinedAble<T["default"]> extends boolean
+			? "boolean"
+			: TypeUtils.NonUndefinedAble<T["default"]> extends number
+				? "number"
+				: never;
+	value: TypeUtils.NonUndefinedAble<T["default"]>;
+};
+
+export type PageVar =
+	| {
+			name: string;
+			type: "string";
+			value: string;
+	  }
+	| {
+			name: string;
+			type: "boolean";
+			value: boolean;
+	  }
+	| {
+			name: string;
+			type: "number";
+			value: number;
+	  };
diff --git a/packages/firefish-js/src/api.types.ts b/packages/firefish-js/src/api.types.ts
index 6d82658c57..fc0407b906 100644
--- a/packages/firefish-js/src/api.types.ts
+++ b/packages/firefish-js/src/api.types.ts
@@ -263,7 +263,7 @@ export type Endpoints = {
 
 	// clips
 	"clips/add-note": { req: TODO; res: TODO };
-	"clips/create": { req: TODO; res: TODO };
+	"clips/create": { req: TODO; res: Clip };
 	"clips/delete": { req: { clipId: Clip["id"] }; res: null };
 	"clips/list": { req: TODO; res: TODO };
 	"clips/notes": { req: TODO; res: TODO };
@@ -748,6 +748,18 @@ export type Endpoints = {
 		};
 		res: Note[];
 	};
+	"notes/thread-muting/create": {
+		req: {
+			noteId: Note["id"];
+		};
+		res: null;
+	};
+	"notes/thread-muting/delete": {
+		req: {
+			noteId: Note["id"];
+		};
+		res: null;
+	};
 	"notes/hybrid-timeline": {
 		req: {
 			limit?: number;
@@ -768,6 +780,12 @@ export type Endpoints = {
 		};
 		res: Note[];
 	};
+	"notes/make-private": {
+		req: {
+			noteId: Note["id"];
+		};
+		res: null;
+	}
 	"notes/mentions": {
 		req: {
 			following?: boolean;
diff --git a/packages/firefish-js/src/type-utils.ts b/packages/firefish-js/src/type-utils.ts
index bb2d843f45..d28675c55c 100644
--- a/packages/firefish-js/src/type-utils.ts
+++ b/packages/firefish-js/src/type-utils.ts
@@ -5,3 +5,5 @@ export type PropertyOfType<Type, U> = {
 }[keyof Type];
 
 export type EndpointsOf<T> = PropertyOfType<Endpoints, { res: T }>;
+
+export type NonUndefinedAble<T> = T extends undefined ? never : T;