Skip to content

Commit

Permalink
feat: search page
Browse files Browse the repository at this point in the history
  • Loading branch information
Udit-takkar committed Mar 24, 2024
1 parent 10573ec commit ab49cec
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 33 deletions.
35 changes: 7 additions & 28 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
"use client";

import Image from "next/image";
import { cn } from "@/lib/utils";
import { BackgroundBeams } from "@/components/background-beams";
import { Input } from "@/components/ui/input";
import { HoverBorderGradient } from "@/components/hover-border-gradient";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import SearchProfilesComponent from "@/components/SearchProfile";
import { useRouter } from "next/navigation";

export default function Home() {
const [searchQuery, setSearchQuery] = useState("");
return (
<div className="h-screen w-full rounded-md bg-neutral-950 relative flex flex-col items-center justify-center antialiased">
<div className="z-50 max-w-4xl mx-auto p-4">
Expand All @@ -23,18 +19,7 @@ export default function Home() {
<ClaimYourProfileButton />
</div>
<div className="flex w-full min-w-lg items-center space-x-2 mt-16">
<Input
type="text"
placeholder="Enter username, name or ethereum address"
className="rounded-xl w-[30rem] h-[2.5rem]"
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
}}
/>
<Button variant="shimmer" size="lg">
<IconSearch /> Search Profile
</Button>
<SearchProfilesComponent />
</div>
</div>
<BackgroundBeams />
Expand All @@ -43,25 +28,19 @@ export default function Home() {
}

export function ClaimYourProfileButton() {
const router = useRouter();
return (
<div className="flex justify-center text-center">
<HoverBorderGradient
containerClassName="rounded-full"
as="button"
className="dark:bg-black bg-white text-black dark:text-white flex items-center space-x-2"
onClick={() => {
router.push("https://enter.metagame.wtf/");
}}
>
<span>Claim your profile</span>
</HoverBorderGradient>
</div>
);
}

function IconSearch() {
return (
<div className="mx-1">
<svg viewBox="0 0 1024 1024" fill="currentColor" height="1em" width="1em">
<path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z" />
</svg>
</div>
);
}
46 changes: 46 additions & 0 deletions app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use client";

import { useSearchParams } from "next/navigation";
import SearchProfilesComponent from "@/components/SearchProfile";
import { useQuery } from "@apollo/client";
import { searchProfiles } from "@/services/apollo";
import { HoverEffect } from "@/components/card-hover-effect";
import { BackgroundBeams } from "@/components/background-beams";
import { toHTTP } from "@/utils/ipfs";

const Page: React.FC = () => {
const searchParams = useSearchParams();
const searchQuery = searchParams.get("query") ?? undefined;

const { loading, error, data } = useQuery(searchProfiles, {
variables: { search: `%${searchQuery}%` },
});

const players = data?.player ?? [];
const formattedData = players.map((player) => {
const { profile } = player;
return {
name: profile.name,
description: profile.description,
username: profile.username,
imageUrl: toHTTP(profile?.profileImageURL ?? ""),
ethereumAddress: player.ethereumAddress,
href: `/${player.ethereumAddress}`,
};
});
console.log("searchParams", players);

return (
<main>
<div className="mt-16">
<SearchProfilesComponent val={searchQuery} />
</div>
<div className="max-w-5xl mx-auto px-8">
<HoverEffect items={formattedData} />
</div>
<BackgroundBeams />
</main>
);
};

export default Page;
11 changes: 11 additions & 0 deletions components/IconSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const IconSearch = () => {
return (
<div className="mx-1">
<svg viewBox="0 0 1024 1024" fill="currentColor" height="1em" width="1em">
<path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z" />
</svg>
</div>
);
};

export default IconSearch;
48 changes: 48 additions & 0 deletions components/SearchProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use client";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { useState } from "react";
import IconSearch from "@/components/IconSearch";
import { useRouter } from "next/navigation";
import { cn } from "@/lib/utils";

const SearchProfile = ({
val,
classname,
}: {
val?: string;
classname?: string;
}) => {
const [searchQuery, setSearchQuery] = useState(val ?? "");
const router = useRouter();

return (
<div
className={cn(
"z-50 flex justify-center items-center space-x-2",
classname
)}
>
<Input
type="text"
placeholder="Enter username, name or ethereum address"
className="rounded-xl w-[30rem] h-[2.5rem]"
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
}}
/>
<Button
onClick={() => {
router.push(`/search?query=${searchQuery}`);
}}
variant="shimmer"
size="shimmerLg"
>
<IconSearch /> Search Profile
</Button>
</div>
);
};

export default SearchProfile;
4 changes: 2 additions & 2 deletions components/background-beams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ export const BackgroundBeams = React.memo(
return (
<div
className={cn(
"absolute h-full w-full inset-0 [mask-size:40px] [mask-repeat:no-repeat] flex items-center justify-center",
"absolute h-full w-full inset-0 [mask-size:40px] [mask-repeat:no-repeat] flex items-center justify-center z-[-1]",
className
)}
>
<svg
className=" z-0 h-full w-full pointer-events-none absolute "
className=" z-[-1] h-full w-full pointer-events-none absolute "
width="100%"
height="100%"
viewBox="0 0 696 316"
Expand Down
129 changes: 129 additions & 0 deletions components/card-hover-effect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"use client";

import { cn } from "@/lib/utils";
import { AnimatePresence, motion } from "framer-motion";
import Link from "next/link";
import { useState } from "react";

export const HoverEffect = ({
items,
className,
}: {
items: {
name: string;
description: string;
href: string;
username: string;
imageUrl: string;
ethereumAddress: string;
}[];
className?: string;
}) => {
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null);

return (
<div
className={cn(
"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 py-10",
className
)}
>
{items.map((item, idx) => (
<Link
href={item?.href}
key={item?.href}
className="relative group block p-2 h-full w-full"
onMouseEnter={() => setHoveredIndex(idx)}
onMouseLeave={() => setHoveredIndex(null)}
target="_blank"
>
<AnimatePresence>
{hoveredIndex === idx && (
<motion.span
className="absolute inset-0 h-full w-full bg-neutral-200 dark:bg-slate-800/[0.8] block rounded-3xl"
layoutId="hoverBackground"
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.15 },
}}
exit={{
opacity: 0,
transition: { duration: 0.15, delay: 0.2 },
}}
/>
)}
</AnimatePresence>
<Card>
<div className="h-20 w-20">
<img
className="rounded-full h-20 w-20 border border-[12px] border-[rgba(255,255,255,0.04)]"
alt="Picture of the author"
src={item.imageUrl}
width={80}
height={80}
/>
</div>

<CardTitle>
{item.name} (@{item.username})
</CardTitle>
<CardDescription>{item.description}</CardDescription>
</Card>
</Link>
))}
</div>
);
};

export const Card = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<div
className={cn(
"rounded-2xl h-full w-full p-4 overflow-hidden bg-black border border-transparent dark:border-white/[0.2] group-hover:border-slate-700 relative z-20",
className
)}
>
<div className="relative z-50">
<div className="p-4">{children}</div>
</div>
</div>
);
};
export const CardTitle = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<h4 className={cn("text-zinc-100 font-bold tracking-wide", className)}>
{children}
</h4>
);
};
export const CardDescription = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<p
className={cn(
"mt-4 text-zinc-400 tracking-wide leading-relaxed text-sm",
className
)}
>
{children}
</p>
);
};
2 changes: 1 addition & 1 deletion components/hover-border-gradient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function HoverBorderGradient({
</div>
<motion.div
className={cn(
"flex-none inset-0 overflow-hidden absolute z-0 rounded-[inherit]"
"flex-none inset-0 overflow-hidden absolute z-[-1] rounded-[inherit]"
)}
style={{
filter: "blur(2px)",
Expand Down
5 changes: 3 additions & 2 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ const buttonVariants = cva(
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
shimmer:
"inline-flex rounded-lg animate-shimmer items-center justify-center rounded-md border border-slate-800 bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] px-6 font-medium text-slate-400 transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50",
"inline-flex rounded-lg animate-shimmer items-center justify-center rounded-md border border-slate-800 bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] px-4 font-medium text-slate-400 transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
shimmerLg: "h-10 rounded-xl px-4",
icon: "h-9",
},
},
defaultVariants: {
Expand Down
44 changes: 44 additions & 0 deletions services/apollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,47 @@ export const profileQuery = gql`
}
}
`;

export const searchProfiles = gql`
query SearchPlayers(
$search: String!
$forLoginDisplay: Boolean! = false
$limit: Int = 20
) {
player(
where: {
_or: [
{
profile: {
username: { _ilike: $search }
name: { _ilike: $search }
}
}
{ ethereumAddress: { _ilike: $search } }
]
}
limit: $limit
) {
ethereumAddress
links {
name
type
url
}
profile {
username
name
profileImageURL
description
timeZone
}
guilds {
Guild {
guildname
name
logo
}
}
}
}
`;

0 comments on commit ab49cec

Please sign in to comment.