diff --git a/.changeset/pretty-planets-attend.md b/.changeset/pretty-planets-attend.md new file mode 100644 index 000000000..621495e4c --- /dev/null +++ b/.changeset/pretty-planets-attend.md @@ -0,0 +1,5 @@ +--- +"@carrot-kpi/react": minor +--- + +Improve typings for serializable KPI token and oracle states diff --git a/.changeset/young-lobsters-shout.md b/.changeset/young-lobsters-shout.md new file mode 100644 index 000000000..c17b33031 --- /dev/null +++ b/.changeset/young-lobsters-shout.md @@ -0,0 +1,5 @@ +--- +"@carrot-kpi/host-frontend": minor +--- + +Integrate new React library KPI token and oracle state typings diff --git a/packages/frontend/src/pages/create-with-template-id/index.tsx b/packages/frontend/src/pages/create-with-template-id/index.tsx index 938966682..8447bf5c5 100644 --- a/packages/frontend/src/pages/create-with-template-id/index.tsx +++ b/packages/frontend/src/pages/create-with-template-id/index.tsx @@ -3,6 +3,7 @@ import { KPITokenCreationForm, useIPFSGatewayURL, usePreferDecentralization, + type SerializableObject, } from "@carrot-kpi/react"; import { useLocation, useNavigate, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; @@ -16,9 +17,8 @@ import { useInvalidateLatestKPITokens } from "../../hooks/useInvalidateLatestKPI import { Layout } from "../../components/layout"; import { Permissioned } from "../../components/permissioned"; import { useIsCreatorAllowed } from "../../hooks/useIsCreatorAllowed"; -import type { Serializable } from "@carrot-kpi/react"; -export const CreateWithTemplateId = () => { +export function CreateWithTemplateId>() { const { i18n, t } = useTranslation(); const { state } = useLocation(); const navigate = useNavigate(); @@ -39,10 +39,10 @@ export const CreateWithTemplateId = () => { const [formKey, setFormKey] = useState(0); const [draftState, setDraftState] = useState<{ templateId?: number; - state: Serializable; + state: S; }>({ templateId: undefined, - state: {}, + state: {} as S, }); const { allowed: creatorAllowed, loading: loadingPermission } = @@ -133,7 +133,7 @@ export const CreateWithTemplateId = () => { templateId, ]); - const handleChange = useCallback((state: Serializable) => { + const handleChange = useCallback((state: S) => { setDraftState((prevState) => ({ templateId: prevState.templateId, state, @@ -209,4 +209,4 @@ export const CreateWithTemplateId = () => { ); -}; +} diff --git a/packages/react/src/components/kpi-token-creation-form.tsx b/packages/react/src/components/kpi-token-creation-form.tsx index 95eee9b8f..b2036aa5e 100644 --- a/packages/react/src/components/kpi-token-creation-form.tsx +++ b/packages/react/src/components/kpi-token-creation-form.tsx @@ -2,11 +2,11 @@ import React from "react"; import type { ReactElement } from "react"; import type { KPITokenCreationFormProps, - Serializable, + SerializableObject, } from "../types/templates"; import { TemplateComponent } from "./template-component"; -export function KPITokenCreationForm({ +export function KPITokenCreationForm>({ template, fallback, error, diff --git a/packages/react/src/components/oracle-creation-form.tsx b/packages/react/src/components/oracle-creation-form.tsx index 879a70a22..6162b240f 100644 --- a/packages/react/src/components/oracle-creation-form.tsx +++ b/packages/react/src/components/oracle-creation-form.tsx @@ -1,9 +1,12 @@ import React from "react"; import type { ReactElement } from "react"; -import type { OracleCreationFormProps, Serializable } from "../types/templates"; +import type { + OracleCreationFormProps, + SerializableObject, +} from "../types/templates"; import { TemplateComponent } from "./template-component"; -export function OracleCreationForm({ +export function OracleCreationForm>({ template, fallback, error, diff --git a/packages/react/src/hooks/useTemplateModule.ts b/packages/react/src/hooks/useTemplateModule.ts index 0cdcc043f..b8a90a320 100644 --- a/packages/react/src/hooks/useTemplateModule.ts +++ b/packages/react/src/hooks/useTemplateModule.ts @@ -14,6 +14,7 @@ import { useStagingMode } from "./useStagingMode"; import { useNetwork } from "wagmi"; import type { RemoteComponentProps, + SerializableObject, TemplateEntity, TemplateType, } from "../types/templates"; @@ -21,10 +22,15 @@ import type { type ComponentType< E extends TemplateEntity, T extends TemplateType, -> = FunctionComponent>; + S extends SerializableObject, +> = FunctionComponent>; -interface CachedModule { - Component: ComponentType; +interface CachedModule< + E extends TemplateEntity, + T extends TemplateType, + S extends SerializableObject, +> { + Component: ComponentType; bundle: TemplateBundle; } @@ -38,15 +44,16 @@ interface TemplateModuleParams { export const useTemplateModule = < E extends TemplateEntity, T extends TemplateType, + S extends SerializableObject, >( params: TemplateModuleParams, ): { loading: boolean; - Component: ComponentType | null; + Component: ComponentType | null; bundle: TemplateBundle | null; } => { const { entity, type, entry, template } = params; - const MODULE_CACHE = useRef>>({}); + const MODULE_CACHE = useRef>>({}); const customBaseURL = useSelector< State, @@ -75,7 +82,7 @@ export const useTemplateModule = < const [loadingExport, setLoadingExport] = useState(false); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [Component, setComponent] = useState | null>( + const [Component, setComponent] = useState | null>( entry && MODULE_CACHE.current[entry] ? () => MODULE_CACHE.current[entry].Component : null, diff --git a/packages/react/src/types/templates.ts b/packages/react/src/types/templates.ts index 2af87ef67..fe26a20e5 100644 --- a/packages/react/src/types/templates.ts +++ b/packages/react/src/types/templates.ts @@ -11,13 +11,22 @@ import { ResolvedTemplate } from "@carrot-kpi/sdk"; import type { ReactElement, ReactNode } from "react"; import type { Hex } from "viem"; -export type Serializable = - | null +export type Serializable = T extends | string | number | boolean - | { [key: string | symbol | number]: Serializable } - | Serializable[]; + | null + | undefined + ? T + : T extends object + ? { [K in keyof T]: Serializable } + : T extends Array + ? Array> + : never; + +export type SerializableObject = { + [K in keyof T]: Serializable; +}; export type TemplateEntity = "kpiToken" | "oracle"; @@ -54,49 +63,47 @@ export interface OracleInitializationBundle { export type OracleInitializationBundleGetter = () => Promise; -export type OracleChangeCallback = ( +export type OracleChangeCallback> = ( internalState: S, initializationBundleGetter?: OracleInitializationBundleGetter, ) => void; -export interface AdditionalRemoteOracleCreationFormProps< - S extends Serializable = Serializable, -> { +export type AdditionalRemoteOracleCreationFormProps< + S extends SerializableObject, +> = { state: S; kpiToken?: Partial; onChange: OracleChangeCallback; navigate: NavigateFunction; onTx: (tx: Tx) => void; -} +}; -export type OracleRemoteCreationFormProps< - S extends Serializable = Serializable, -> = BaseRemoteTemplateComponentProps & - AdditionalRemoteOracleCreationFormProps; +export type OracleRemoteCreationFormProps> = + BaseRemoteTemplateComponentProps & + AdditionalRemoteOracleCreationFormProps; -export type KPITokenChangeCallback = ( +export type KPITokenChangeCallback> = ( state: S, ) => void; -export type OracleCreationFormProps = +export type OracleCreationFormProps> = TemplateComponentProps & AdditionalRemoteOracleCreationFormProps; -export interface AdditionalRemoteKPITokenCreationFormProps< - S extends Serializable = Serializable, -> { +export type AdditionalRemoteKPITokenCreationFormProps< + S extends SerializableObject, +> = { state: S; onChange: KPITokenChangeCallback; onCreate: () => void; navigate: NavigateFunction; onTx: (tx: Tx) => void; -} +}; -export type KPITokenRemoteCreationFormProps< - S extends Serializable = Serializable, -> = BaseRemoteTemplateComponentProps & - AdditionalRemoteKPITokenCreationFormProps; +export type KPITokenRemoteCreationFormProps> = + BaseRemoteTemplateComponentProps & + AdditionalRemoteKPITokenCreationFormProps; -export type KPITokenCreationFormProps = +export type KPITokenCreationFormProps> = TemplateComponentProps & AdditionalRemoteKPITokenCreationFormProps; export interface AdditionalRemoteOraclePageProps { @@ -125,7 +132,7 @@ export type KPITokenPageProps = TemplateComponentProps & export type RemoteComponentProps< E extends TemplateEntity, T extends TemplateType, - S extends Serializable = Serializable, + S extends SerializableObject, > = E extends "kpiToken" ? T extends "creationForm" ? KPITokenRemoteCreationFormProps