113 lines
2.9 KiB
TypeScript
113 lines
2.9 KiB
TypeScript
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
|
|
}
|
|
|
|
return feed.title
|
|
}
|
|
|
|
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}`
|
|
setOpenInAppDeeplink({
|
|
deeplink: deeplinkUrl,
|
|
fallbackUrl: fallback ? fallback() : undefined,
|
|
})
|
|
})
|
|
}
|
|
|
|
export const openInFollowApp = ({
|
|
deeplink,
|
|
fallback,
|
|
alwaysFallback,
|
|
fallbackUrl,
|
|
}: {
|
|
deeplink: string
|
|
fallback: () => void
|
|
alwaysFallback?: boolean
|
|
fallbackUrl?: string
|
|
}): Promise<boolean> => {
|
|
return new Promise((resolve) => {
|
|
const timeout = 500
|
|
let isAppOpened = false
|
|
|
|
const handleBlur = () => {
|
|
isAppOpened = true
|
|
cleanup()
|
|
resolve(true)
|
|
}
|
|
|
|
const cleanup = () => {
|
|
window.removeEventListener("blur", handleBlur)
|
|
}
|
|
|
|
window.addEventListener("blur", handleBlur)
|
|
|
|
const deeplinkUrl = `${DEEPLINK_SCHEME}${deeplink}`
|
|
console.info("Open deeplink:", deeplinkUrl)
|
|
window.location.href = deeplinkUrl
|
|
|
|
setTimeout(() => {
|
|
cleanup()
|
|
if (!isAppOpened && !alwaysFallback) {
|
|
fallback()
|
|
resolve(false)
|
|
return
|
|
}
|
|
setOpenInAppDeeplink({
|
|
deeplink: deeplinkUrl,
|
|
fallbackUrl,
|
|
})
|
|
}, timeout)
|
|
})
|
|
}
|