Skip to content

Commit

Permalink
feat: use new composer action hook in debugger
Browse files Browse the repository at this point in the history
  • Loading branch information
michalkvasnicak committed Nov 5, 2024
1 parent 9f105d7 commit 5b2945e
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 540 deletions.
98 changes: 6 additions & 92 deletions packages/debugger/app/components/action-debugger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { cn } from "@/lib/utils";
import {
type FarcasterFrameContext,
type FrameActionBodyPayload,
OnComposeFormActionFuncReturnType,
defaultTheme,
} from "@frames.js/render";
import { ParsingReport } from "frames.js";
Expand All @@ -26,7 +25,6 @@ import React, {
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
} from "react";
import { Button } from "../../@/components/ui/button";
Expand All @@ -37,12 +35,9 @@ import { useFrame } from "@frames.js/render/use-frame";
import { WithTooltip } from "./with-tooltip";
import { useToast } from "@/components/ui/use-toast";
import type { CastActionDefinitionResponse } from "../frames/route";
import { ComposerFormActionDialog } from "./composer-form-action-dialog";
import { AwaitableController } from "../lib/awaitable-controller";
import type { ComposerActionFormResponse } from "frames.js/types";
import { CastComposer, CastComposerRef } from "./cast-composer";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import type { FarcasterSigner } from "@frames.js/render/identity/farcaster";
import { ComposerActionDebugger } from "./composer-action-debugger";

type FrameDebuggerFramePropertiesTableRowsProps = {
actionMetadataItem: CastActionDefinitionResponse;
Expand Down Expand Up @@ -227,47 +222,9 @@ export const ActionDebugger = React.forwardRef<
}
}, [copySuccess, setCopySuccess]);

const [composeFormActionDialogSignal, setComposerFormActionDialogSignal] =
useState<AwaitableController<
OnComposeFormActionFuncReturnType,
ComposerActionFormResponse
> | null>(null);
const actionFrameState = useFrame({
...farcasterFrameConfig,
async onComposerFormAction({ form }) {
try {
const dialogSignal = new AwaitableController<
OnComposeFormActionFuncReturnType,
ComposerActionFormResponse
>(form);

setComposerFormActionDialogSignal(dialogSignal);

const result = await dialogSignal;

// if result is undefined then user closed the dialog window without submitting
// otherwise we have updated data
if (result?.composerActionState) {
castComposerRef.current?.updateState(result.composerActionState);
}

return result;
} catch (e) {
console.error(e);
toast({
title: "Error occurred",
description:
e instanceof Error
? e.message
: "Unexpected error, check the console for more info",
variant: "destructive",
});
} finally {
setComposerFormActionDialogSignal(null);
}
},
});
const castComposerRef = useRef<CastComposerRef>(null);
const [castActionDefinition, setCastActionDefinition] = useState<Exclude<
CastActionDefinitionResponse,
{ status: "failure" }
Expand Down Expand Up @@ -401,57 +358,14 @@ export const ActionDebugger = React.forwardRef<
actionMetadataItem={actionMetadataItem}
onRefreshUrl={() => refreshUrl()}
>
<CastComposer
farcasterFrameConfig={farcasterFrameConfig}
ref={castComposerRef}
composerAction={actionMetadataItem.action}
onComposerActionClick={(composerActionState) => {
if (actionMetadataItem.status !== "success") {
console.error(actionMetadataItem);

toast({
title: "Invalid action metadata",
description:
"Please check the console for more information",
variant: "destructive",
});
return;
}

Promise.resolve(
actionFrameState.onComposerActionButtonPress({
castAction: {
...actionMetadataItem.action,
url: actionMetadataItem.url,
},
composerActionState,
// clear stack, this removes first item that will appear in the debugger
clearStack: true,
})
).catch((e: unknown) => {
// eslint-disable-next-line no-console -- provide feedback to the user
console.error(e);
});
<ComposerActionDebugger
actionMetadata={actionMetadataItem.action}
url={actionMetadataItem.url}
onToggleToCastActionDebugger={() => {
setActiveTab("cast-action");
}}
/>
</ActionInfo>

{!!composeFormActionDialogSignal && (
<ComposerFormActionDialog
connectedAddress={farcasterFrameConfig.connectedAddress}
composerActionForm={composeFormActionDialogSignal.data}
onClose={() => {
composeFormActionDialogSignal.resolve(undefined);
}}
onSave={({ composerState }) => {
composeFormActionDialogSignal.resolve({
composerActionState: composerState,
});
}}
onTransaction={farcasterFrameConfig.onTransaction}
onSignature={farcasterFrameConfig.onSignature}
/>
)}
</TabsContent>
</Tabs>
</>
Expand Down
48 changes: 18 additions & 30 deletions packages/debugger/app/components/cast-composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,21 @@ import {
ExternalLinkIcon,
} from "lucide-react";
import IconByName from "./octicons";
import { useFrame } from "@frames.js/render/use-frame";
import { useFrame_unstable } from "@frames.js/render/use-frame";
import { WithTooltip } from "./with-tooltip";
import type {
FarcasterFrameContext,
FrameActionBodyPayload,
FrameStackDone,
} from "@frames.js/render";
import { fallbackFrameContext } from "@frames.js/render";
import { FrameUI } from "./frame-ui";
import { useToast } from "@/components/ui/use-toast";
import { ToastAction } from "@radix-ui/react-toast";
import Link from "next/link";
import type { FarcasterSigner } from "@frames.js/render/identity/farcaster";
import { useDebuggerFrameState } from "@frames.js/render/unstable-use-debugger-frame-state";
import { useFarcasterIdentity } from "../hooks/useFarcasterIdentity";
import { useAccount } from "wagmi";
import { FrameStackDone } from "@frames.js/render/unstable-types";

type CastComposerProps = {
composerAction: Partial<ComposerActionResponse>;
onComposerActionClick: (state: ComposerActionState) => any;
farcasterFrameConfig: Parameters<
typeof useFrame<
FarcasterSigner | null,
FrameActionBodyPayload,
FarcasterFrameContext
>
>[0];
};

export type CastComposerRef = {
Expand All @@ -43,7 +35,7 @@ export type CastComposerRef = {
export const CastComposer = React.forwardRef<
CastComposerRef,
CastComposerProps
>(({ composerAction, farcasterFrameConfig, onComposerActionClick }, ref) => {
>(({ composerAction, onComposerActionClick }, ref) => {
const [state, setState] = useState<ComposerActionState>({
text: "",
embeds: [],
Expand Down Expand Up @@ -79,7 +71,6 @@ export const CastComposer = React.forwardRef<
{state.embeds.slice(0, 2).map((embed, index) => (
<li key={`${embed}-${index}`}>
<CastEmbedPreview
farcasterFrameConfig={farcasterFrameConfig}
onRemove={() => {
const filteredEmbeds = state.embeds.filter(
(_, i) => i !== index
Expand Down Expand Up @@ -119,13 +110,6 @@ export const CastComposer = React.forwardRef<
CastComposer.displayName = "CastComposer";

type CastEmbedPreviewProps = {
farcasterFrameConfig: Parameters<
typeof useFrame<
FarcasterSigner | null,
FrameActionBodyPayload,
FarcasterFrameContext
>
>[0];
url: string;
onRemove: () => void;
};
Expand All @@ -147,15 +131,19 @@ function isAtLeastPartialFrame(stackItem: FrameStackDone): boolean {
);
}

function CastEmbedPreview({
farcasterFrameConfig,
onRemove,
url,
}: CastEmbedPreviewProps) {
function CastEmbedPreview({ onRemove, url }: CastEmbedPreviewProps) {
const account = useAccount();
const { toast } = useToast();
const frame = useFrame({
...farcasterFrameConfig,
const farcasterIdentity = useFarcasterIdentity();
const frame = useFrame_unstable({
frameStateHook: useDebuggerFrameState,
connectedAddress: account.address,
homeframeUrl: url,
frameActionProxy: "/frames",
frameGetProxy: "/frames",
resolveSigner() {
return farcasterIdentity.withContext(fallbackFrameContext);
},
});

const handleFrameError = useCallback(
Expand Down
51 changes: 51 additions & 0 deletions packages/debugger/app/components/composer-action-debugger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type {
ComposerActionResponse,
ComposerActionState,
} from "frames.js/types";
import { CastComposer, CastComposerRef } from "./cast-composer";
import { useRef, useState } from "react";
import { ComposerFormActionDialog } from "./composer-form-action-dialog";
import { useFarcasterIdentity } from "../hooks/useFarcasterIdentity";

type ComposerActionDebuggerProps = {
url: string;
actionMetadata: Partial<ComposerActionResponse>;
onToggleToCastActionDebugger: () => void;
};

export function ComposerActionDebugger({
actionMetadata,
url,
onToggleToCastActionDebugger,
}: ComposerActionDebuggerProps) {
const castComposerRef = useRef<CastComposerRef>(null);
const signer = useFarcasterIdentity();
const [actionState, setActionState] = useState<ComposerActionState | null>(
null
);

return (
<>
<CastComposer
composerAction={actionMetadata}
onComposerActionClick={setActionState}
ref={castComposerRef}
/>
{!!actionState && (
<ComposerFormActionDialog
actionState={actionState}
signer={signer}
url={url}
onClose={() => {
setActionState(null);
}}
onSubmit={(newActionState) => {
castComposerRef.current?.updateState(newActionState);
setActionState(null);
}}
onToggleToCastActionDebugger={onToggleToCastActionDebugger}
/>
)}
</>
);
}
Loading

0 comments on commit 5b2945e

Please sign in to comment.