The recipes.blue monorepo recipes.blue
recipes appview atproto
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: author route

+153 -27
+20 -11
apps/api/src/xrpc/index.ts
··· 1 1 import { Hono } from 'hono'; 2 2 import { db, recipeTable } from '@cookware/database'; 3 3 import { and, eq, sql } from 'drizzle-orm'; 4 - import { getDidDoc, getDidFromHandleOrDid } from '@cookware/lexicons'; 4 + import { DID, getDidDoc, getDidFromHandleOrDid, parseDid } from '@cookware/lexicons'; 5 5 6 6 export const xrpcApp = new Hono(); 7 7 8 8 xrpcApp.get('/moe.hayden.cookware.getRecipes', async ctx => { 9 - const recipes = await db.select({ 10 - rkey: recipeTable.rkey, 11 - title: recipeTable.title, 12 - description: recipeTable.description, 13 - ingredientsCount: sql`json_array_length(${recipeTable.ingredients})`, 14 - stepsCount: sql`json_array_length(${recipeTable.steps})`, 15 - createdAt: recipeTable.createdAt, 16 - authorDid: recipeTable.authorDid, 17 - uri: sql`concat(${recipeTable.authorDid}, "/", ${recipeTable.rkey})`.as('uri'), 18 - }).from(recipeTable); 9 + const { did: didQuery } = ctx.req.query(); 10 + 11 + let did: DID | null = null; 12 + if (didQuery) 13 + did = await getDidFromHandleOrDid(didQuery); 14 + 15 + const recipes = await db 16 + .select({ 17 + rkey: recipeTable.rkey, 18 + title: recipeTable.title, 19 + description: recipeTable.description, 20 + ingredientsCount: sql`json_array_length(${recipeTable.ingredients})`, 21 + stepsCount: sql`json_array_length(${recipeTable.steps})`, 22 + createdAt: recipeTable.createdAt, 23 + authorDid: recipeTable.authorDid, 24 + uri: sql`concat(${recipeTable.authorDid}, "/", ${recipeTable.rkey})`.as('uri'), 25 + }) 26 + .from(recipeTable) 27 + .where(did ? eq(recipeTable.authorDid, did) : undefined); 19 28 20 29 const results = []; 21 30 const eachRecipe = async (r: typeof recipes[0]) => ({
+12 -12
apps/web/src/index.css
··· 10 10 --card-foreground: 0 0% 3.9%; 11 11 --popover: 0 0% 100%; 12 12 --popover-foreground: 0 0% 3.9%; 13 - --primary: 0 72.2% 50.6%; 14 - --primary-foreground: 0 85.7% 97.3%; 15 - --secondary: 0 0% 96.1%; 16 - --secondary-foreground: 0 0% 9%; 13 + --primary: 221.2 83.2% 53.3%; 14 + --primary-foreground: 210 40% 98%; 15 + --secondary: 210 40% 96.1%; 16 + --secondary-foreground: 222.2 47.4% 11.2%; 17 17 --muted: 0 0% 96.1%; 18 18 --muted-foreground: 0 0% 45.1%; 19 - --accent: 0 0% 96.1%; 20 - --accent-foreground: 0 0% 9%; 19 + --accent: 210 40% 96.1%; 20 + --accent-foreground: 222.2 47.4% 11.2%; 21 21 --destructive: 0 84.2% 60.2%; 22 22 --destructive-foreground: 0 0% 98%; 23 - --border: 0 0% 89.8%; 24 - --input: 0 0% 89.8%; 25 - --ring: 0 72.2% 50.6%; 23 + --border: 214.3 31.8% 91.4%; 24 + --input: 214.3 31.8% 91.4%; 25 + --ring: 221.2 83.2% 53.3%; 26 26 --radius: 0.5rem; 27 27 --chart-1: 12 76% 61%; 28 28 --chart-2: 173 58% 39%; ··· 31 31 --chart-5: 27 87% 67%; 32 32 --sidebar-background: 0 0% 98%; 33 33 --sidebar-foreground: 240 5.3% 26.1%; 34 - --sidebar-primary: 240 5.9% 10%; 34 + --sidebar-primary: 221.2 83.2% 53.3%; 35 35 --sidebar-primary-foreground: 0 0% 98%; 36 - --sidebar-accent: 240 4.8% 95.9%; 36 + --sidebar-accent: 210 40% 96.1%; 37 37 --sidebar-accent-foreground: 240 5.9% 10%; 38 38 --sidebar-border: 220 13% 91%; 39 - --sidebar-ring: 217.2 91.2% 59.8%; 39 + --sidebar-ring: 221.2 83.2% 53.3%; 40 40 } 41 41 .dark { 42 42 --background: 0 0% 3.9%;
+45 -3
apps/web/src/routeTree.gen.ts
··· 21 21 const appIndexLazyImport = createFileRoute('/_/(app)/')() 22 22 const authLoginLazyImport = createFileRoute('/_/(auth)/login')() 23 23 const appRecipesNewLazyImport = createFileRoute('/_/(app)/recipes/new')() 24 + const appRecipesAuthorIndexLazyImport = createFileRoute( 25 + '/_/(app)/recipes/$author/', 26 + )() 24 27 25 28 // Create/Update Routes 26 29 ··· 53 56 } as any) 54 57 .lazy(() => import('./routes/_.(app)/recipes/new.lazy').then((d) => d.Route)) 55 58 59 + const appRecipesAuthorIndexLazyRoute = appRecipesAuthorIndexLazyImport 60 + .update({ 61 + id: '/(app)/recipes/$author/', 62 + path: '/recipes/$author/', 63 + getParentRoute: () => Route, 64 + } as any) 65 + .lazy(() => 66 + import('./routes/_.(app)/recipes/$author/index.lazy').then((d) => d.Route), 67 + ) 68 + 56 69 const appRecipesAuthorRkeyRoute = appRecipesAuthorRkeyImport.update({ 57 70 id: '/(app)/recipes/$author/$rkey', 58 71 path: '/recipes/$author/$rkey', ··· 98 111 preLoaderRoute: typeof appRecipesAuthorRkeyImport 99 112 parentRoute: typeof rootRoute 100 113 } 114 + '/_/(app)/recipes/$author/': { 115 + id: '/_/(app)/recipes/$author/' 116 + path: '/recipes/$author' 117 + fullPath: '/recipes/$author' 118 + preLoaderRoute: typeof appRecipesAuthorIndexLazyImport 119 + parentRoute: typeof rootRoute 120 + } 101 121 } 102 122 } 103 123 ··· 108 128 appIndexLazyRoute: typeof appIndexLazyRoute 109 129 appRecipesNewLazyRoute: typeof appRecipesNewLazyRoute 110 130 appRecipesAuthorRkeyRoute: typeof appRecipesAuthorRkeyRoute 131 + appRecipesAuthorIndexLazyRoute: typeof appRecipesAuthorIndexLazyRoute 111 132 } 112 133 113 134 const RouteChildren: RouteChildren = { ··· 115 136 appIndexLazyRoute: appIndexLazyRoute, 116 137 appRecipesNewLazyRoute: appRecipesNewLazyRoute, 117 138 appRecipesAuthorRkeyRoute: appRecipesAuthorRkeyRoute, 139 + appRecipesAuthorIndexLazyRoute: appRecipesAuthorIndexLazyRoute, 118 140 } 119 141 120 142 const RouteWithChildren = Route._addFileChildren(RouteChildren) ··· 125 147 '/': typeof appIndexLazyRoute 126 148 '/recipes/new': typeof appRecipesNewLazyRoute 127 149 '/recipes/$author/$rkey': typeof appRecipesAuthorRkeyRoute 150 + '/recipes/$author': typeof appRecipesAuthorIndexLazyRoute 128 151 } 129 152 130 153 export interface FileRoutesByTo { ··· 132 155 '/': typeof appIndexLazyRoute 133 156 '/recipes/new': typeof appRecipesNewLazyRoute 134 157 '/recipes/$author/$rkey': typeof appRecipesAuthorRkeyRoute 158 + '/recipes/$author': typeof appRecipesAuthorIndexLazyRoute 135 159 } 136 160 137 161 export interface FileRoutesById { ··· 141 165 '/_/(app)/': typeof appIndexLazyRoute 142 166 '/_/(app)/recipes/new': typeof appRecipesNewLazyRoute 143 167 '/_/(app)/recipes/$author/$rkey': typeof appRecipesAuthorRkeyRoute 168 + '/_/(app)/recipes/$author/': typeof appRecipesAuthorIndexLazyRoute 144 169 } 145 170 146 171 export interface FileRouteTypes { 147 172 fileRoutesByFullPath: FileRoutesByFullPath 148 - fullPaths: '' | '/login' | '/' | '/recipes/new' | '/recipes/$author/$rkey' 173 + fullPaths: 174 + | '' 175 + | '/login' 176 + | '/' 177 + | '/recipes/new' 178 + | '/recipes/$author/$rkey' 179 + | '/recipes/$author' 149 180 fileRoutesByTo: FileRoutesByTo 150 - to: '/login' | '/' | '/recipes/new' | '/recipes/$author/$rkey' 181 + to: 182 + | '/login' 183 + | '/' 184 + | '/recipes/new' 185 + | '/recipes/$author/$rkey' 186 + | '/recipes/$author' 151 187 id: 152 188 | '__root__' 153 189 | '/_' ··· 155 191 | '/_/(app)/' 156 192 | '/_/(app)/recipes/new' 157 193 | '/_/(app)/recipes/$author/$rkey' 194 + | '/_/(app)/recipes/$author/' 158 195 fileRoutesById: FileRoutesById 159 196 } 160 197 ··· 185 222 "/_/(auth)/login", 186 223 "/_/(app)/", 187 224 "/_/(app)/recipes/new", 188 - "/_/(app)/recipes/$author/$rkey" 225 + "/_/(app)/recipes/$author/$rkey", 226 + "/_/(app)/recipes/$author/" 189 227 ] 190 228 }, 191 229 "/_/(auth)/login": { ··· 202 240 }, 203 241 "/_/(app)/recipes/$author/$rkey": { 204 242 "filePath": "_.(app)/recipes/$author/$rkey.tsx", 243 + "parent": "/_" 244 + }, 245 + "/_/(app)/recipes/$author/": { 246 + "filePath": "_.(app)/recipes/$author/index.lazy.tsx", 205 247 "parent": "/_" 206 248 } 207 249 }
+1 -1
apps/web/src/routes/_.(app)/recipes/$author/$rkey.tsx
··· 53 53 <BreadcrumbSeparator className="hidden md:block" /> 54 54 <BreadcrumbItem className="hidden md:block"> 55 55 <BreadcrumbLink asChild> 56 - <Link href={`/profiles/${recipe.author.handle}`}> 56 + <Link to="/recipes/$author" params={{ author: recipe.author.handle }}> 57 57 {recipe.author.handle} 58 58 </Link> 59 59 </BreadcrumbLink>
+75
apps/web/src/routes/_.(app)/recipes/$author/index.lazy.tsx
··· 1 + import { createLazyFileRoute, Link } from '@tanstack/react-router' 2 + import { 3 + Breadcrumb, 4 + BreadcrumbItem, 5 + BreadcrumbLink, 6 + BreadcrumbList, 7 + BreadcrumbPage, 8 + BreadcrumbSeparator, 9 + } from '@/components/ui/breadcrumb' 10 + import { Separator } from '@/components/ui/separator' 11 + import { SidebarTrigger } from '@/components/ui/sidebar' 12 + import QueryPlaceholder from '@/components/query-placeholder' 13 + import { useRecipesQuery } from '@/queries/recipe' 14 + import { RecipeCard } from '@/screens/Recipes/RecipeCard' 15 + 16 + export const Route = createLazyFileRoute('/_/(app)/recipes/$author/')({ 17 + component: RouteComponent, 18 + }) 19 + 20 + function RouteComponent() { 21 + const { author } = Route.useParams() 22 + const query = useRecipesQuery('', author) 23 + 24 + return ( 25 + <> 26 + <header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12"> 27 + <div className="flex items-center gap-2 px-4"> 28 + <SidebarTrigger className="-ml-1" /> 29 + <Separator orientation="vertical" className="mr-2 h-4" /> 30 + <Breadcrumb> 31 + <BreadcrumbList> 32 + <BreadcrumbItem className="hidden md:block"> 33 + <BreadcrumbLink asChild> 34 + <Link href="/">Community</Link> 35 + </BreadcrumbLink> 36 + </BreadcrumbItem> 37 + <BreadcrumbSeparator className="hidden md:block" /> 38 + <BreadcrumbItem> 39 + <BreadcrumbLink asChild> 40 + <Link to="/">Browse Recipes</Link> 41 + </BreadcrumbLink> 42 + </BreadcrumbItem> 43 + <BreadcrumbSeparator className="hidden md:block" /> 44 + <BreadcrumbItem className="hidden md:block"> 45 + <BreadcrumbPage>{author}</BreadcrumbPage> 46 + </BreadcrumbItem> 47 + </BreadcrumbList> 48 + </Breadcrumb> 49 + </div> 50 + </header> 51 + <div className="flex flex-col gap-4 p-4 pt-6 items-center"> 52 + <h1 className="text-4xl font-black">Community Recipes!</h1> 53 + <p className="text-lg">See what the community's been cooking.</p> 54 + </div> 55 + <div className="flex-1 flex flex-col items-center p-4"> 56 + <div className="flex flex-col gap-4 max-w-2xl w-full items-center"> 57 + <QueryPlaceholder query={query} cards cardsCount={12}> 58 + {query.data?.recipes.map((recipe, idx) => ( 59 + <RecipeCard 60 + title={recipe.title} 61 + description={recipe.description} 62 + rkey={recipe.rkey} 63 + author={recipe.author} 64 + time={{ amount: 30, unit: 'min' }} 65 + steps={recipe.steps} 66 + ingredients={recipe.ingredients} 67 + key={idx} 68 + /> 69 + ))} 70 + </QueryPlaceholder> 71 + </div> 72 + </div> 73 + </> 74 + ) 75 + }