From cb6a65b7cc1651d86b83b5b4089a7145cd59b0be Mon Sep 17 00:00:00 2001 From: Innei Date: Sun, 3 Nov 2024 20:52:26 +0800 Subject: [PATCH] feat: add hydrate data type-safe helper Signed-off-by: Innei --- apps/server/client/lib/helper.ts | 41 +++++++++++++++++++ .../pages/(main)/share/feeds/[id]/metadata.ts | 9 +++- apps/server/client/query/feed.ts | 5 +-- apps/server/src/meta-handler.ts | 6 +-- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/apps/server/client/lib/helper.ts b/apps/server/client/lib/helper.ts index 427198567..f714c1d83 100644 --- a/apps/server/client/lib/helper.ts +++ b/apps/server/client/lib/helper.ts @@ -2,6 +2,9 @@ import { setOpenInAppDeeplink } from "@client/atoms/app" import type { FeedOrListRespModel } from "@follow/models/types" import { DEEPLINK_SCHEME } from "@follow/shared/constants" +import type { ApiClient } from "~/lib/api-client" +import type { defineMetadata } from "~/meta-handler" + export const getPreferredTitle = (feed?: FeedOrListRespModel | null) => { if (!feed?.id) { return feed?.title @@ -14,6 +17,44 @@ export const getHydrateData = (key: string) => { return window.__HYDRATE__?.[key] } +// type ExtractHydrateData = T extends readonly (infer Item)[] +// ? Item extends { readonly type: "hydrate"; readonly data: infer D } +// ? D +// : never +// : never + +// export type GetHydrateData = T extends (...args: any[]) => Promise +// ? ExtractHydrateData +// : T extends Promise +// ? ExtractHydrateData +// : ExtractHydrateData + +type ExtractHydrateData = T extends readonly (infer Item)[] + ? Item extends { readonly type: "hydrate"; readonly data: infer D } + ? D + : never + : never + +type UnwrapMetadataFn = + T extends

>(args: { + params: P + apiClient: ApiClient + origin: string + throwError: (status: number, message: any) => never + }) => Promise | infer R + ? R + : never + +export type GetHydrateData = T extends (...args: any[]) => Promise + ? ExtractHydrateData + : T extends (...args: any[]) => infer R + ? ExtractHydrateData + : T extends Promise + ? ExtractHydrateData + : T extends typeof defineMetadata + ? ExtractHydrateData[0]>> + : ExtractHydrateData + export const askOpenInFollowApp = (deeplink: string, fallback?: () => string): Promise => { return new Promise(() => { const deeplinkUrl = `${DEEPLINK_SCHEME}${deeplink}` diff --git a/apps/server/client/pages/(main)/share/feeds/[id]/metadata.ts b/apps/server/client/pages/(main)/share/feeds/[id]/metadata.ts index 97b59116a..a5fa424f0 100644 --- a/apps/server/client/pages/(main)/share/feeds/[id]/metadata.ts +++ b/apps/server/client/pages/(main)/share/feeds/[id]/metadata.ts @@ -1,6 +1,8 @@ +import type { GetHydrateData } from "@client/lib/helper" + import { defineMetadata } from "~/meta-handler" -export default defineMetadata(async ({ params, apiClient, origin, throwError }) => { +const meta = defineMetadata(async ({ params, apiClient, origin, throwError }) => { const feedId = params.id const feed = await apiClient.feeds.$get({ query: { id: feedId } }).catch((e) => { @@ -27,5 +29,8 @@ export default defineMetadata(async ({ params, apiClient, origin, throwError }) path: apiClient.feeds.$url({ query: { id: feedId } }).pathname, key: `feeds.$get,query:id=${feedId}`, }, - ] + ] as const }) + +export type FeedHydrateData = GetHydrateData +export default meta diff --git a/apps/server/client/query/feed.ts b/apps/server/client/query/feed.ts index d347ff0ee..29de0c907 100644 --- a/apps/server/client/query/feed.ts +++ b/apps/server/client/query/feed.ts @@ -1,5 +1,6 @@ import { apiClient } from "@client/lib/api-fetch" import { getHydrateData } from "@client/lib/helper" +import type { FeedHydrateData } from "@client/pages/(main)/share/feeds/[id]/metadata" import { useQuery } from "@tanstack/react-query" async function fetchFeedById(id: string) { @@ -14,9 +15,7 @@ export const useFeed = ({ id }: { id: string }) => useQuery({ queryKey: ["feed", id], queryFn: () => fetchFeedById(id), - initialData: getHydrateData(`feeds.$get,query:id=${id}`) as any as Awaited< - ReturnType - >, + initialData: getHydrateData(`feeds.$get,query:id=${id}`) as FeedHydrateData, }) export type Feed = Awaited> diff --git a/apps/server/src/meta-handler.ts b/apps/server/src/meta-handler.ts index 71ac60f8a..852f746bf 100644 --- a/apps/server/src/meta-handler.ts +++ b/apps/server/src/meta-handler.ts @@ -68,22 +68,20 @@ export async function injectMetaHandler( return [] } -export function defineMetadata>( +export function defineMetadata, T extends readonly MetaTag[]>( fn: (args: { req: FastifyRequest url: URL params: Params apiClient: ApiClient origin: string - setStatus: (status: number) => void setStatusText: (statusText: string) => void throwError: (status: number, message: any) => never - }) => MetaTag[] | Promise, + }) => Promise | T, ) { return fn } - export class MetaError extends Error { status: number metaMessage: object