Skip to content

Commit

Permalink
feat: first base implementation of a drafts feature (#472)
Browse files Browse the repository at this point in the history
* feat(ui): export number format values type from number input

* chore: add changeset

* feat(sdk): split full and partial kpi token specification types

* feat(sdk): introduce serializable type and kpi token internal state types

* feat(frontend): introduce kpi token creation form state management

---------

Co-authored-by: luzzifoss <fedeluzzi00@gmail.com>
  • Loading branch information
luzzif and luzzifoss committed Oct 30, 2023
1 parent 7808812 commit 50a1bbc
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/slimy-sloths-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@carrot-kpi/host-frontend": minor
---

Introduce internal state management for KPI token creation forms.
6 changes: 6 additions & 0 deletions .changeset/smooth-rice-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@carrot-kpi/react": minor
---

Include state and state change handler in KPI token creation form types and
introduce Serializable type to represent internal template states
5 changes: 5 additions & 0 deletions .changeset/strange-beds-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@carrot-kpi/ui": patch
---

Export NumberFormatValues type from number input component
6 changes: 6 additions & 0 deletions .changeset/thick-apes-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@carrot-kpi/sdk": minor
---

Split KPITokenSpecification and FullKPITokenSpecification into 2 different
types, respectively without and with the ipfsHash property
32 changes: 29 additions & 3 deletions packages/frontend/src/pages/create-with-template-id/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ 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 = () => {
const { i18n, t } = useTranslation();
Expand All @@ -29,13 +30,20 @@ export const CreateWithTemplateId = () => {
const preferDecentralization = usePreferDecentralization();
const ipfsGatewayURL = useIPFSGatewayURL();
const invalidateLatestKPITokens = useInvalidateLatestKPITokens();
const pinningProxyAuthenticated = useIsPinningProxyAuthenticated();

const [loading, setLoading] = useState(true);
const [template, setTemplate] = useState<ResolvedTemplate | null>(
state && "specification" in state.template ? state.template : null,
);
const [formKey, setFormKey] = useState(0);
const pinningProxyAuthenticated = useIsPinningProxyAuthenticated();
const [draftState, setDraftState] = useState<{
templateId?: number;
state: Serializable;
}>({
templateId: undefined,
state: {},
});

const { allowed: creatorAllowed, loading: loadingPermission } =
useIsCreatorAllowed(address);
Expand All @@ -54,6 +62,10 @@ export const CreateWithTemplateId = () => {

useEffect(() => {
if (!!state?.template) {
setDraftState((previousState) => ({
templateId: state.template.id,
state: previousState.state,
}));
setTemplate(state.template);
return;
}
Expand Down Expand Up @@ -93,7 +105,12 @@ export const CreateWithTemplateId = () => {
if (!cancelled) setTemplate(null);
return;
}
if (!cancelled) setTemplate(resolvedTemplates[0]);
const resolvedTemplate = resolvedTemplates[0];
setDraftState((previousState) => ({
templateId: resolvedTemplate.id,
state: previousState.state,
}));
if (!cancelled) setTemplate(resolvedTemplate);
} catch (error) {
console.error(
`could not fetch template with id ${templateId}`,
Expand All @@ -116,6 +133,13 @@ export const CreateWithTemplateId = () => {
templateId,
]);

const handleChange = useCallback((state: Serializable) => {
setDraftState((prevState) => ({
templateId: prevState.templateId,
state,
}));
}, []);

const handleCreate = useCallback(() => {
invalidateLatestKPITokens();
}, [invalidateLatestKPITokens]);
Expand All @@ -142,7 +166,7 @@ export const CreateWithTemplateId = () => {
) : template ? (
<KPITokenCreationForm
key={formKey}
template={template || undefined}
template={template}
fallback={
<div className="h-screen py-20 text-black flex justify-center">
<Loader />
Expand All @@ -164,6 +188,8 @@ export const CreateWithTemplateId = () => {
}
i18n={i18n}
className={{ root: "w-full h-full" }}
state={draftState.state}
onChange={handleChange}
onCreate={handleCreate}
navigate={navigate}
onTx={addTransaction}
Expand Down
9 changes: 6 additions & 3 deletions packages/react/src/components/kpi-token-creation-form.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import React from "react";
import type { ReactElement } from "react";
import type { KPITokenCreationFormProps } from "../types/templates";
import type {
KPITokenCreationFormProps,
Serializable,
} from "../types/templates";
import { TemplateComponent } from "./template-component";

export function KPITokenCreationForm({
export function KPITokenCreationForm<S extends Serializable = Serializable>({
template,
fallback,
error,
i18n,
className,
...additionalProps
}: KPITokenCreationFormProps): ReactElement {
}: KPITokenCreationFormProps<S>): ReactElement {
return (
<TemplateComponent
entity="kpiToken"
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/components/oracle-creation-form.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import type { ReactElement } from "react";
import type { OracleCreationFormProps } from "../types/templates";
import type { OracleCreationFormProps, Serializable } from "../types/templates";
import { TemplateComponent } from "./template-component";

export function OracleCreationForm<S>({
export function OracleCreationForm<S extends Serializable = Serializable>({
template,
fallback,
error,
Expand Down
56 changes: 39 additions & 17 deletions packages/react/src/types/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import { ResolvedTemplate } from "@carrot-kpi/sdk";
import type { ReactElement, ReactNode } from "react";
import type { Hex } from "viem";

export type Serializable =
| null
| string
| number
| boolean
| { [key: string | symbol | number]: Serializable }
| Serializable[];

export type TemplateEntity = "kpiToken" | "oracle";

export type TemplateType = "creationForm" | "page";
Expand Down Expand Up @@ -46,37 +54,50 @@ export interface OracleInitializationBundle {
export type OracleInitializationBundleGetter =
() => Promise<OracleInitializationBundle>;

export type OracleChangeCallback<T> = (
internalState: Partial<T>,
export type OracleChangeCallback<S extends Serializable = Serializable> = (
internalState: S,
initializationBundleGetter?: OracleInitializationBundleGetter,
) => void;

export interface AdditionalRemoteOracleCreationFormProps<T> {
state: Partial<T>;
export interface AdditionalRemoteOracleCreationFormProps<
S extends Serializable = Serializable,
> {
state: S;
kpiToken?: Partial<KPIToken>;
onChange: OracleChangeCallback<T>;
onChange: OracleChangeCallback<S>;
navigate: NavigateFunction;
onTx: <T extends TxType>(tx: Tx<T>) => void;
}

export type OracleRemoteCreationFormProps<T> =
BaseRemoteTemplateComponentProps &
AdditionalRemoteOracleCreationFormProps<T>;

export type OracleCreationFormProps<S> = TemplateComponentProps &
export type OracleRemoteCreationFormProps<
S extends Serializable = Serializable,
> = BaseRemoteTemplateComponentProps &
AdditionalRemoteOracleCreationFormProps<S>;

export interface AdditionalRemoteKPITokenCreationFormProps {
export type KPITokenChangeCallback<S extends Serializable = Serializable> = (
state: S,
) => void;

export type OracleCreationFormProps<S extends Serializable = Serializable> =
TemplateComponentProps & AdditionalRemoteOracleCreationFormProps<S>;

export interface AdditionalRemoteKPITokenCreationFormProps<
S extends Serializable = Serializable,
> {
state: S;
onChange: KPITokenChangeCallback<S>;
onCreate: () => void;
navigate: NavigateFunction;
onTx: <T extends TxType>(tx: Tx<T>) => void;
}

export type KPITokenRemoteCreationFormProps = BaseRemoteTemplateComponentProps &
AdditionalRemoteKPITokenCreationFormProps;
export type KPITokenRemoteCreationFormProps<
S extends Serializable = Serializable,
> = BaseRemoteTemplateComponentProps &
AdditionalRemoteKPITokenCreationFormProps<S>;

export type KPITokenCreationFormProps = TemplateComponentProps &
AdditionalRemoteKPITokenCreationFormProps;
export type KPITokenCreationFormProps<S extends Serializable = Serializable> =
TemplateComponentProps & AdditionalRemoteKPITokenCreationFormProps<S>;

export interface AdditionalRemoteOraclePageProps {
oracle?: ResolvedOracleWithData | null;
Expand Down Expand Up @@ -104,12 +125,13 @@ export type KPITokenPageProps = TemplateComponentProps &
export type RemoteComponentProps<
E extends TemplateEntity,
T extends TemplateType,
S extends Serializable = Serializable,
> = E extends "kpiToken"
? T extends "creationForm"
? KPITokenRemoteCreationFormProps
? KPITokenRemoteCreationFormProps<S>
: KPITokenRemotePageProps
: E extends "oracle"
? T extends "creationForm"
? OracleRemoteCreationFormProps<unknown>
? OracleRemoteCreationFormProps<S>
: OracleRemotePageProps
: never;
11 changes: 7 additions & 4 deletions packages/sdk/src/entities/kpi-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import { ChainId } from "../commons";
import { type Address, type Hex } from "viem";

export interface KPITokenSpecification {
ipfsHash: string;
title: string;
description: string;
tags: string[];
}

export interface FullKPITokenSpecification extends KPITokenSpecification {
ipfsHash: string;
}

export class BaseKPIToken {
constructor(
public readonly chainId: ChainId,
Expand Down Expand Up @@ -57,7 +60,7 @@ export class ResolvedKPIToken extends BaseKPIToken {
owner: Address,
public readonly template: ResolvedTemplate,
public readonly oracles: ResolvedOracle[],
public readonly specification: KPITokenSpecification,
public readonly specification: FullKPITokenSpecification,
expiration: number,
creationTimestamp: number,
finalized: boolean,
Expand All @@ -76,7 +79,7 @@ export class ResolvedKPIToken extends BaseKPIToken {
kpiToken: KPIToken,
template: ResolvedTemplate,
oracles: ResolvedOracle[],
specification: KPITokenSpecification,
specification: FullKPITokenSpecification,
) {
return new ResolvedKPIToken(
kpiToken.chainId,
Expand All @@ -99,7 +102,7 @@ export class ResolvedKPITokenWithData extends BaseKPIToken {
owner: Address,
public readonly template: ResolvedTemplate,
public readonly oracles: ResolvedOracleWithData[],
public readonly specification: KPITokenSpecification,
public readonly specification: FullKPITokenSpecification,
expiration: number,
creationTimestamp: number,
finalized: boolean,
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/components/number-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
BaseInputWrapper,
} from "./commons/input";

export { type NumberFormatValues } from "react-number-format";

export type NumberInputProps = Omit<
NumericFormatProps & BaseInputProps<string>,
"size" | "id" | "className"
Expand Down

2 comments on commit 50a1bbc

@vercel
Copy link

@vercel vercel bot commented on 50a1bbc Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

host-frontend – ./packages/frontend

host-frontend-git-main-carrot-labs.vercel.app
host-frontend-carrot-labs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 50a1bbc Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

ui – ./packages/ui

ui-carrot-labs.vercel.app
ui-git-main-carrot-labs.vercel.app

Please sign in to comment.