Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multi specification support #517

Merged
merged 27 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
199d535
feat: parse all specs at once
michalkvasnicak Oct 24, 2024
0bbcc19
feat: handle multi protocol result in render package
michalkvasnicak Oct 24, 2024
740ca39
chore: make use frame backward compatible
michalkvasnicak Oct 25, 2024
a6014cf
feat: unstable multi specification api
michalkvasnicak Oct 25, 2024
96dc0be
feat: support new unstable api in debugger
michalkvasnicak Oct 25, 2024
fbbed31
feat: add useful properties
michalkvasnicak Oct 25, 2024
d464a37
chore: remove composer and cast actions code
michalkvasnicak Oct 28, 2024
e4f0e8e
refactor: use resolveSigner function
michalkvasnicak Oct 28, 2024
cb3a10d
chore: export unstable hook from use-frame
michalkvasnicak Oct 28, 2024
36f5d1f
revert: debugger
michalkvasnicak Oct 28, 2024
96db514
fix: incorrect specification
michalkvasnicak Oct 28, 2024
23b9b4a
fix: call signerless press if user doesn't have a signer
michalkvasnicak Oct 31, 2024
aa58e18
feat: customizable frame state hook
michalkvasnicak Oct 31, 2024
7b457a1
fix: make hooks generic
michalkvasnicak Nov 4, 2024
3900284
fix: allow any frame state in frame ui
michalkvasnicak Nov 4, 2024
c977e3a
chore: changeset
michalkvasnicak Nov 5, 2024
c3f9dc8
feat: mini app support
michalkvasnicak Nov 4, 2024
0a0dddf
feat: add resolveAddress function
michalkvasnicak Nov 5, 2024
4719393
feat: send form to post message handler
michalkvasnicak Nov 5, 2024
6ebc342
feat: use new composer action hook in debugger
michalkvasnicak Nov 5, 2024
cc31edf
chore: changeset
michalkvasnicak Nov 5, 2024
f624858
feat: export message listener and parser
michalkvasnicak Nov 5, 2024
c38ef09
feat: allow to customize fetch function
michalkvasnicak Nov 5, 2024
baa3f2d
chore: rename function
michalkvasnicak Nov 19, 2024
9208bcf
fix: types
michalkvasnicak Nov 19, 2024
b2d7f22
chore: use resolveAddress instead of connectedAddress
michalkvasnicak Nov 19, 2024
49c1f7e
chore: move debugger frame state hook to debugger
michalkvasnicak Nov 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cuddly-rivers-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@frames.js/render": patch
---

feat: customizable frame state for useFrame hook
6 changes: 6 additions & 0 deletions .changeset/grumpy-berries-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@frames.js/debugger": patch
"@frames.js/render": patch
---

feat: useComposerAction hook
7 changes: 7 additions & 0 deletions .changeset/twenty-pugs-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"frames.js": patch
"@frames.js/debugger": patch
"@frames.js/render": patch
---

feat: multi specification support
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
50 changes: 20 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 { useFarcasterIdentity } from "../hooks/useFarcasterIdentity";
import { useAccount } from "wagmi";
import { FrameStackDone } from "@frames.js/render/unstable-types";
import { useDebuggerFrameState } from "../hooks/useDebuggerFrameState";

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,21 @@ 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,
async resolveAddress() {
return account.address ?? null;
},
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
Loading