Skip to content

Commit

Permalink
feat: move queries to query hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
hbjydev committed Dec 9, 2024
1 parent 052c1b9 commit 635b5d0
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 123 deletions.
11 changes: 10 additions & 1 deletion apps/web/src/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {
SidebarFooter,
SidebarRail,
} from "@/components/ui/sidebar"
import { Suspense } from "react"
import { Button } from "./ui/button"
import { Link } from "@tanstack/react-router"

const data = {
user: {
Expand Down Expand Up @@ -75,7 +78,13 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<NavMain items={data.navMain} />
</SidebarContent>
<SidebarFooter>
<NavUser />
<Suspense fallback={
<Button asChild>
<Link href="/login" className="w-full">Log in</Link>
</Button>
} name="nav-user-data">
<NavUser />
</Suspense>
</SidebarFooter>
<SidebarRail />
</Sidebar>
Expand Down
141 changes: 79 additions & 62 deletions apps/web/src/components/nav-user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ChevronsUpDown,
LogOut,
} from "lucide-react"

import {
Avatar,
AvatarFallback,
Expand All @@ -26,83 +25,101 @@ import {
SidebarMenuItem,
useSidebar,
} from "@/components/ui/sidebar"
import { useQuery } from "@tanstack/react-query"
import axios from "axios";
import type { AppBskyActorGetProfile } from "@atcute/client/lexicons"
import QueryPlaceholder from "./query-placeholder"
import { useUserQuery } from "@/queries/self"
import { Button } from "./ui/button"
import { Link } from "@tanstack/react-router"
import { Skeleton } from "./ui/skeleton"

export function NavUser() {
const { isMobile } = useSidebar()

const userQuery = useQuery({
queryKey: ['/oauth/me'],
queryFn: async () => {
const res = await axios.get<AppBskyActorGetProfile.Output>('/oauth/me');
return res.data;
},
});
const { data } = userQuery;
const userQuery = useUserQuery();

return (
<SidebarMenu>
<SidebarMenuItem>
<QueryPlaceholder query={userQuery}
noData={
if (userQuery.isLoading) {
return (
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Skeleton className="h-8 w-8 rounded-lg" />
<div className="grid flex-1 text-left text-sm leading-tight">
<Skeleton className="h-2 w-20 rounded-lg" />
</div>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
)
}

if (userQuery.isError || !userQuery.data) {
return (
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Button asChild>
<Link href="/login" className="w-full">Log in</Link>
</Button>
}
>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
);
}

return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={userQuery.data.avatar} alt={userQuery.data.displayName ?? `@${userQuery.data.handle}`} />
<AvatarFallback className="rounded-lg">{userQuery.data.handle.substring(2)}</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{userQuery.data.displayName ?? `@${userQuery.data.handle}`}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={data?.avatar} alt={data?.displayName ?? `@${data?.handle}`} />
<AvatarFallback className="rounded-lg">{data?.handle.substring(2)}</AvatarFallback>
<AvatarImage src={userQuery.data.avatar} alt={userQuery.data.displayName ?? `@${userQuery.data.handle}`} />
<AvatarFallback className="rounded-lg">{userQuery.data.handle.substring(2)}</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{data?.displayName ?? `@${data?.handle}`}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={data?.avatar} alt={data?.displayName ?? `@${data?.handle}`} />
<AvatarFallback className="rounded-lg">{data?.handle.substring(2)}</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{data?.displayName ?? `@${data?.handle}`}</span>
</div>
<span className="truncate font-semibold">{userQuery.data.displayName ?? `@${userQuery.data.handle}`}</span>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<LogOut />
Log out
<BadgeCheck />
Account
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</QueryPlaceholder>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
)
Expand Down
10 changes: 9 additions & 1 deletion apps/web/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ declare module '@tanstack/react-router' {

const creds = new CredentialManager({ service: `https://${SERVER_URL}/api/` });
const rpc = new XRPC({ handler: creds });
const queryClient = new QueryClient();
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
structuralSharing: false,
retry: false,
}
}
});

createRoot(document.getElementById('root')!).render(
<StrictMode>
Expand Down
31 changes: 31 additions & 0 deletions apps/web/src/queries/recipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useXrpc } from "@/hooks/use-xrpc";
import { useQuery } from "@tanstack/react-query";

const RQKEY_ROOT = 'posts';
export const RQKEY = (cursor: string, did: string, rkey: string) => [RQKEY_ROOT, cursor, did, rkey];

export const useRecipesQuery = (cursor: string, did?: string) => {
const { rpc } = useXrpc();
return useQuery({
queryKey: RQKEY(cursor, '', ''),
queryFn: async () => {
const res = await rpc.get('moe.hayden.cookware.getRecipes', {
params: { cursor, did },
});
return res.data;
},
});
};

export const useRecipeQuery = (did: string, rkey: string) => {
const { rpc } = useXrpc();
return useQuery({
queryKey: RQKEY('', did, rkey),
queryFn: async () => {
const res = await rpc.get('moe.hayden.cookware.getRecipe', {
params: { did, rkey },
});
return res.data;
},
});
};
13 changes: 13 additions & 0 deletions apps/web/src/queries/self.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { AppBskyActorGetProfile } from "@atcute/client/lexicons";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";

export const useUserQuery = () => {
return useQuery({
queryKey: ['self'],
queryFn: async () => {
const res = await axios.get<AppBskyActorGetProfile.Output>('/oauth/me');
return res.data;
},
});
}
57 changes: 14 additions & 43 deletions apps/web/src/routes/(app)/index.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,16 @@ import {
} from '@/components/ui/breadcrumb'
import { Separator } from '@/components/ui/separator'
import { SidebarTrigger } from '@/components/ui/sidebar'
import { useQuery } from '@tanstack/react-query'
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card'
import { useXrpc } from '@/hooks/use-xrpc'
import { Clock, CookingPot, ListIcon } from 'lucide-react'
import QueryPlaceholder from '@/components/query-placeholder'
import { useRecipesQuery } from '@/queries/recipe'
import { RecipeCard } from '@/screens/Recipes/RecipeCard'

export const Route = createLazyFileRoute('/(app)/')({
component: RouteComponent,
})

function RouteComponent() {
const { rpc } = useXrpc()

const query = useQuery({
queryKey: ['moe.hayden.cookware.getRecipes', { cursor: '' }],
queryFn: () =>
rpc.get('moe.hayden.cookware.getRecipes', {
params: { cursor: '' },
}),
})
const query = useRecipesQuery('');

return (
<>
Expand All @@ -58,30 +42,17 @@ function RouteComponent() {
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4">
<QueryPlaceholder query={query} cards cardsCount={12}>
{query.data?.data.recipes.map((v, idx) => (
<Link key={idx} href={`/recipes/${v.author}/${v.rkey}`}>
<Card>
<CardHeader>
<CardTitle>{v.title}</CardTitle>
</CardHeader>
<CardContent>
<p>{v.description}</p>
</CardContent>
<CardFooter className="flex gap-6 text-sm text-muted-foreground">
<span className="flex items-center gap-2">
<ListIcon className="size-4" /> <span>{v.steps}</span>
</span>

<span className="flex items-center gap-2">
<CookingPot className="size-4" /> <span>{v.ingredients}</span>
</span>

<span className="flex items-center gap-2">
<Clock className="size-4" /> <span>30min.</span>
</span>
</CardFooter>
</Card>
</Link>
{query.data?.recipes.map((recipe, idx) => (
<RecipeCard
title={recipe.title}
description={recipe.description}
rkey={recipe.rkey}
author={recipe.author}
time={{ amount: 30, unit: 'min' }}
steps={recipe.steps}
ingredients={recipe.ingredients}
key={idx}
/>
))}
</QueryPlaceholder>
</div>
Expand Down
Loading

0 comments on commit 635b5d0

Please sign in to comment.