feat: add hydrate data type-safe helper

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2024-11-03 20:52:26 +08:00
parent 8372d38c1c
commit cb6a65b7cc
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
4 changed files with 52 additions and 9 deletions

View File

@ -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> = T extends readonly (infer Item)[]
// ? Item extends { readonly type: "hydrate"; readonly data: infer D }
// ? D
// : never
// : never
// export type GetHydrateData<T> = T extends (...args: any[]) => Promise<infer R>
// ? ExtractHydrateData<R>
// : T extends Promise<infer R>
// ? ExtractHydrateData<R>
// : ExtractHydrateData<T>
type ExtractHydrateData<T> = T extends readonly (infer Item)[]
? Item extends { readonly type: "hydrate"; readonly data: infer D }
? D
: never
: never
type UnwrapMetadataFn<T> =
T extends <P extends Record<string, string>>(args: {
params: P
apiClient: ApiClient
origin: string
throwError: (status: number, message: any) => never
}) => Promise<infer R> | infer R
? R
: never
export type GetHydrateData<T> = T extends (...args: any[]) => Promise<infer R>
? ExtractHydrateData<R>
: T extends (...args: any[]) => infer R
? ExtractHydrateData<R>
: T extends Promise<infer R>
? ExtractHydrateData<R>
: T extends typeof defineMetadata
? ExtractHydrateData<UnwrapMetadataFn<Parameters<T>[0]>>
: ExtractHydrateData<T>
export const askOpenInFollowApp = (deeplink: string, fallback?: () => string): Promise<boolean> => {
return new Promise(() => {
const deeplinkUrl = `${DEEPLINK_SCHEME}${deeplink}`

View File

@ -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<typeof meta>
export default meta

View File

@ -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<typeof fetchFeedById>
>,
initialData: getHydrateData(`feeds.$get,query:id=${id}`) as FeedHydrateData,
})
export type Feed = Awaited<ReturnType<typeof fetchFeedById>>

View File

@ -68,22 +68,20 @@ export async function injectMetaHandler(
return []
}
export function defineMetadata<Params extends Record<string, string>>(
export function defineMetadata<Params extends Record<string, string>, 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<MetaTag[]>,
}) => Promise<T> | T,
) {
return fn
}
export class MetaError extends Error {
status: number
metaMessage: object