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;