diff --git a/framegear/components/Frame/Frame.tsx b/framegear/components/Frame/Frame.tsx index 1beecca031..5ad8c5d78a 100644 --- a/framegear/components/Frame/Frame.tsx +++ b/framegear/components/Frame/Frame.tsx @@ -48,7 +48,11 @@ function ValidFrame({ tags }: { tags: Record }) { return (
{/* eslint-disable-next-line @next/next/no-img-element */} - +
{!!input && ( - {children} + + {children} + ); } diff --git a/framegear/components/ValidationResults/ValidationResults.tsx b/framegear/components/ValidationResults/ValidationResults.tsx index 684085941f..967b4bf095 100644 --- a/framegear/components/ValidationResults/ValidationResults.tsx +++ b/framegear/components/ValidationResults/ValidationResults.tsx @@ -1,9 +1,45 @@ import { frameResultsAtom } from '@/utils/store'; +import { vNextSchema } from '@/utils/validation'; import { useAtom } from 'jotai'; +import { useMemo } from 'react'; +import { type SchemaDescription } from 'yup'; + +const REQUIRED_FIELD_NAMES = Object.entries(vNextSchema.describe().fields).reduce( + (acc, [name, description]) => + (description as SchemaDescription).optional ? acc : [...acc, name], + [] as string[], +); export function ValidationResults() { const [results] = useAtom(frameResultsAtom); + + if (results.length === 0) { + return ; + } + + return ; +} + +function ValidationResultsPlaceholder() { + return ( +
+

Frame validations

+
+

No data yet.

+
+
+ ); +} + +function ValidationResultsContent() { + const [results] = useAtom(frameResultsAtom); const latestResult = results[results.length - 1]; + const optionalTags = useMemo(() => { + const requiredNames = new Set(REQUIRED_FIELD_NAMES); + const tagEntries = Object.entries(latestResult?.tags); + return tagEntries.filter((tag) => !requiredNames.has(tag[0])); + }, [latestResult]); + return (

@@ -15,24 +51,29 @@ export function ValidationResults() { )}

- {latestResult && ( -
- {Object.entries(latestResult.tags).map(([key, value]) => ( +
    + {REQUIRED_FIELD_NAMES.map((name) => { + const value = latestResult.tags[name]; + return ( - ))} -
- )} + ); + })} + {optionalTags.map(([key, value]) => ( + + ))} +
); } -function ValidationEntry({ name, value, error }: { name: string; value: string; error?: string }) { +type ValidationEntryProps = { name: string; value?: string; error?: string }; +function ValidationEntry({ name, value, error }: ValidationEntryProps) { return (
{name} {error ? '🔴' : '🟢'}
-
{value}
+
{value || 'Not set'}
+ {!!error &&
{error}
}
); } diff --git a/framegear/utils/validation.ts b/framegear/utils/validation.ts index 891c3149ae..52b5b3d653 100644 --- a/framegear/utils/validation.ts +++ b/framegear/utils/validation.ts @@ -2,8 +2,8 @@ import * as yup from 'yup'; export const vNextSchema = yup.object({ 'fc:frame': yup.string().required().matches(/vNext/, '"fc:frame" must be "vNext"'), - 'fc:frame:image': yup.string().defined(), - 'og:image': yup.string().defined(), + 'fc:frame:image': yup.string().required(), + 'og:image': yup.string().required(), 'fc:frame:button:1': yup.string().optional(), // TODO: yup doesn't infer type well from this concise definition ...[2, 3, 4].reduce(