Skip to content

Commit

Permalink
feat: always show required tag status (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
cnasc authored Feb 22, 2024
1 parent 0570e68 commit 8b9937e
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 16 deletions.
12 changes: 9 additions & 3 deletions framegear/components/Frame/Frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ function ValidFrame({ tags }: { tags: Record<string, string> }) {
return (
<div>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img className={`w-full rounded-t-xl aspect-[${imageAspectRatio}]`} src={image} alt="" />
<img
className={`w-full rounded-t-xl aspect-[${imageAspectRatio}] object-cover`}
src={image}
alt=""
/>
<div className="bg-content-light flex flex-col gap-2 rounded-b-xl px-4 py-2">
{!!input && (
<input
Expand Down Expand Up @@ -121,12 +125,14 @@ function FrameButton({
}, [button, setResults]);
return (
<button
className="border-button w-[45%] grow rounded-lg border bg-white p-2 text-black"
className="border-button w-[45%] grow rounded-lg border bg-white px-4 py-2 text-black"
type="button"
onClick={handleClick}
disabled={button?.action !== 'post'}
>
<span>{children}</span>
<span className="block max-w-full overflow-hidden text-ellipsis whitespace-nowrap">
{children}
</span>
</button>
);
}
64 changes: 53 additions & 11 deletions framegear/components/ValidationResults/ValidationResults.tsx
Original file line number Diff line number Diff line change
@@ -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 <ValidationResultsPlaceholder />;
}

return <ValidationResultsContent />;
}

function ValidationResultsPlaceholder() {
return (
<div className="flex flex-col gap-4">
<h2>Frame validations</h2>
<div className="bg-content flex w-full flex-col gap-4 rounded-xl p-6">
<p>No data yet.</p>
</div>
</div>
);
}

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 (
<div className="flex flex-col gap-4">
<h2>
Expand All @@ -15,24 +51,29 @@ export function ValidationResults() {
)}
</h2>
<div className="bg-content flex w-full flex-col gap-4 rounded-xl p-6">
{latestResult && (
<dl className="flex flex-col gap-4">
{Object.entries(latestResult.tags).map(([key, value]) => (
<ul className="flex list-none flex-col gap-4 p-0">
{REQUIRED_FIELD_NAMES.map((name) => {
const value = latestResult.tags[name];
return (
<ValidationEntry
key={key}
name={key}
key={name}
name={name}
value={value}
error={latestResult.errors[key]}
error={latestResult.errors[name]}
/>
))}
</dl>
)}
);
})}
{optionalTags.map(([key, value]) => (
<ValidationEntry key={key} name={key} value={value} error={latestResult.errors[key]} />
))}
</ul>
</div>
</div>
);
}

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 (
<div
className={`border-pallette-line flex flex-col gap-2 border-b pb-4 last:border-b-0 last:pb-0`}
Expand All @@ -41,7 +82,8 @@ function ValidationEntry({ name, value, error }: { name: string; value: string;
<span>{name}</span>
<span>{error ? '🔴' : '🟢'}</span>
</div>
<div className="font-mono">{value}</div>
<div className="font-mono">{value || 'Not set'}</div>
{!!error && <div className="font-mono italic">{error}</div>}
</div>
);
}
4 changes: 2 additions & 2 deletions framegear/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 8b9937e

Please sign in to comment.