+10
-5
apps/api/src/xrpc/index.ts
+10
-5
apps/api/src/xrpc/index.ts
···
35
35
handler: simpleFetchHandler({
36
36
service: 'https://public.api.bsky.app',
37
37
}),
38
-
})
38
+
});
39
39
40
40
let authorInfo: BlueRecipesFeedDefs.AuthorInfo | null = null;
41
41
if (did) {
···
94
94
});
95
95
}
96
96
97
-
const author = await getDidDoc(recipe.authorDid);
97
+
const rpc = new XRPC({
98
+
handler: simpleFetchHandler({
99
+
service: 'https://public.api.bsky.app',
100
+
}),
101
+
});
102
+
103
+
const authorInfo = await getAuthorInfo(recipe.authorDid, rpc);
98
104
99
105
return ctx.json({
100
106
recipe: {
101
-
author: {
102
-
handle: author.alsoKnownAs[0]?.substring(5),
103
-
},
107
+
author: authorInfo,
104
108
title: recipe.title,
109
+
time: 5,
105
110
description: recipe.description,
106
111
ingredients: recipe.ingredients,
107
112
steps: recipe.steps,
+28
-32
apps/web/src/routes/_.(app)/recipes/new.tsx
+28
-32
apps/web/src/routes/_.(app)/recipes/new.tsx
···
56
56
component: RouteComponent,
57
57
});
58
58
59
-
const schema = RecipeRecord.extend({
60
-
ingredients: z.array(
61
-
IngredientObject.extend({
62
-
amount: z.coerce.number().nullable(),
63
-
}),
64
-
),
65
-
});
59
+
const schema = RecipeRecord;
66
60
67
61
function RouteComponent() {
68
62
const form = useForm<z.infer<typeof schema>>({
···
136
130
{...field}
137
131
/>
138
132
</FormControl>
133
+
<FormDescription>Describe your recipe, maybe tell the world how tasty it is? (Optional)</FormDescription>
134
+
<FormMessage />
135
+
</FormItem>
136
+
)}
137
+
/>
138
+
139
+
<FormField
140
+
name="time"
141
+
control={form.control}
142
+
render={({ field: { value, ...field } }) => (
143
+
<FormItem>
144
+
<FormLabel>Time</FormLabel>
145
+
<FormControl>
146
+
<Input
147
+
type="number"
148
+
className="resize-none"
149
+
value={value || ""}
150
+
{...field}
151
+
/>
152
+
</FormControl>
153
+
<FormDescription>How long (in minutes) does your recipe take to complete?</FormDescription>
139
154
<FormMessage />
140
155
</FormItem>
141
156
)}
···
151
166
<div className="flex w-full flex-col gap-2">
152
167
{ingredients.fields.map((field, index) => (
153
168
<SortableItem key={field.id} value={field.id} asChild>
154
-
<div className="grid grid-cols-[2rem_1fr_0.2fr_0.2fr_2rem] items-center gap-2">
169
+
<div className="grid grid-cols-[2rem_0.3fr_1fr_2rem] items-center gap-2">
155
170
<SortableDragHandle
156
171
type="button"
157
172
variant="outline"
···
166
181
167
182
<FormField
168
183
control={form.control}
169
-
name={`ingredients.${index}.name`}
170
-
render={({ field }) => (
171
-
<FormItem>
172
-
<FormControl>
173
-
<Input
174
-
placeholder="Ingredient"
175
-
className="h-8"
176
-
{...field}
177
-
/>
178
-
</FormControl>
179
-
<FormMessage />
180
-
</FormItem>
181
-
)}
182
-
/>
183
-
184
-
<FormField
185
-
control={form.control}
186
184
name={`ingredients.${index}.amount`}
187
185
render={({ field: { value, ...field } }) => (
188
186
<FormItem>
189
187
<FormControl>
190
188
<Input
191
-
type="number"
192
-
placeholder="#"
193
-
value={value || "0"}
189
+
placeholder="Amount"
190
+
value={value || ""}
194
191
className="h-8"
195
192
{...field}
196
193
/>
···
202
199
203
200
<FormField
204
201
control={form.control}
205
-
name={`ingredients.${index}.unit`}
206
-
render={({ field: { value, ...field } }) => (
202
+
name={`ingredients.${index}.name`}
203
+
render={({ field }) => (
207
204
<FormItem>
208
205
<FormControl>
209
206
<Input
210
-
placeholder="Unit"
207
+
placeholder="Ingredient"
211
208
className="h-8"
212
-
value={value || ""}
213
209
{...field}
214
210
/>
215
211
</FormControl>
+1
-2
libs/lexicons/src/defs.ts
+1
-2
libs/lexicons/src/defs.ts
···
1
1
import { z } from 'zod';
2
2
3
3
export const IngredientObject = z.object({
4
+
amount: z.string().nullable(),
4
5
name: z.string().max(3000, 'Ingredient names must be under 3000 characters.'),
5
-
amount: z.number().nullable(),
6
-
unit: z.string().max(3000, 'Ingredient units must be under 3000 characters.').nullable(),
7
6
});
8
7
9
8
export type Ingredient = z.infer<typeof IngredientObject>;
+1
libs/lexicons/src/recipe.ts
+1
libs/lexicons/src/recipe.ts
···
6
6
export const RecipeRecord = z.object({
7
7
title: z.string().max(3000, 'Recipe titles must be under 3000 characters.'),
8
8
description: z.string().max(3000, 'Recipe descriptions must be under 3000 characters.').nullable(),
9
+
time: z.number({ message: 'Time must be a number.' }),
9
10
ingredients: z.array(IngredientObject),
10
11
steps: z.array(StepObject),
11
12
});