-
Notifications
You must be signed in to change notification settings - Fork 444
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: add explorer scaffolding * feat: add infinite scroll * feat: trigger fetch using observer * feat: add search * feat: sync search using atoms * fix: prevent rerender by router * fix: prevent render loop * chore: separate useEffect calls * feat: add ssr using tanstack query * refactor: move out consts and fetchers * chore: align to review suggestions * chore: dont revalidate search * chore: replace guild card skeleton * refactor: move out guild card * chore: move v2 search * chore: add header prereqs * chore: remove infinite scroll button * chore: add Toggle and ToggleGroup * chore: hook sticky header together * feat: add sign in button to explorer * fix: adjust navigation alignment * fix: make dialog overlay properly * fix: make call to action responsive * chore: resolve merge conflict * fix(getGuildSearch): throw on error * fix(InfiniteScrollGuilds): properly update the page param * fix(GuildCard): don't show legacy guild logos * fix(StickyNavbar): use native scroll * fix(InfiniteScrollGuilds): revert latest change * fix(StickyNavbar): remove console.log * fix: rename `_components` to `components` * fix(Toggle): colors and variants * fix: sign in buttom styling * final cleanup --------- Co-authored-by: BrickheadJohnny <[email protected]>
- Loading branch information
1 parent
198f502
commit 107df80
Showing
26 changed files
with
839 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// dumping here types util it comes down properly | ||
|
||
export {} | ||
|
||
declare global { | ||
type Guild = { | ||
name: string; | ||
id: string; | ||
urlName: string; | ||
createdAt: number; | ||
updatedAt: number; | ||
description: string; | ||
imageUrl: string; | ||
backgroundImageUrl: string; | ||
visibility: Record<string, string>; | ||
settings: Record<string, string>; | ||
searchTags: string[]; | ||
categoryTags: string[]; | ||
socialLinks: Record<string, string>; | ||
owner: string; | ||
}; | ||
|
||
type PaginatedResponse<Item extends any> = { | ||
page: number; | ||
pageSize: number; | ||
sortBy: string; | ||
reverse: boolean; | ||
searchQuery: string; | ||
query: string; | ||
items: Item[]; | ||
total: number; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { atom } from "jotai"; | ||
import { ACTIVE_SECTION } from "./constants"; | ||
|
||
export const searchAtom = atom<string | undefined>(undefined); | ||
export const isNavStuckAtom = atom(false); | ||
export const isSearchStuckAtom = atom(false); | ||
export const activeSectionAtom = atom< | ||
(typeof ACTIVE_SECTION)[keyof typeof ACTIVE_SECTION] | ||
>(ACTIVE_SECTION.yourGuilds); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { buttonVariants } from "@/components/ui/Button"; | ||
import { Plus } from "@phosphor-icons/react/dist/ssr"; | ||
import Link from "next/link"; | ||
|
||
export const CreateGuildLink = () => ( | ||
<Link | ||
href="/create-guild" | ||
aria-label="Create guild" | ||
prefetch={false} | ||
className={buttonVariants({ | ||
variant: "ghost", | ||
size: "sm", | ||
className: "min-h-11 w-11 gap-1.5 px-0 sm:min-h-0 sm:w-auto sm:px-3", | ||
})} | ||
> | ||
<Plus /> | ||
<span className="hidden sm:inline-block">Create guild</span> | ||
</Link> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
"use client"; | ||
|
||
import { cn } from "@/lib/cssUtils"; | ||
import { useAtomValue } from "jotai"; | ||
import { isNavStuckAtom, isSearchStuckAtom } from "../atoms"; | ||
|
||
export const HeaderBackground = () => { | ||
const isNavStuck = useAtomValue(isNavStuckAtom); | ||
const isSearchStuck = useAtomValue(isSearchStuckAtom); | ||
|
||
return ( | ||
<div | ||
className={cn( | ||
"fixed inset-x-0 top-0 z-10 h-0 bg-card shadow-md transition-all duration-200 dark:bg-background", | ||
{ | ||
"h-16": isNavStuck, | ||
"h-[calc(theme(space.36)+theme(space.2))] bg-gradient-to-b from-card to-background sm:h-[calc(theme(space.28)-theme(space.2))] dark:from-background dark:to-card-secondary/50": | ||
isSearchStuck, | ||
}, | ||
)} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
"use client"; | ||
|
||
import { useInfiniteQuery } from "@tanstack/react-query"; | ||
import { useIntersection } from "foxact/use-intersection"; | ||
import { useAtomValue } from "jotai"; | ||
import { useCallback, useEffect } from "react"; | ||
import { searchAtom } from "../atoms"; | ||
import { PAGE_SIZE } from "../constants"; | ||
import { getGuildSearch } from "../fetchers"; | ||
import { GuildCard, GuildCardSkeleton } from "./GuildCard"; | ||
|
||
export const InfiniteScrollGuilds = () => { | ||
const search = useAtomValue(searchAtom); | ||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = | ||
useInfiniteQuery({ | ||
queryKey: ["guilds", search || ""], | ||
queryFn: getGuildSearch(search), | ||
initialPageParam: 1, | ||
staleTime: Number.POSITIVE_INFINITY, | ||
enabled: search !== undefined, | ||
getNextPageParam: (lastPage) => | ||
lastPage.total / lastPage.pageSize <= lastPage.page | ||
? undefined | ||
: lastPage.page + 1, | ||
}); | ||
|
||
const [setIntersection, isIntersected, resetIsIntersected] = useIntersection({ | ||
rootMargin: "700px", | ||
}); | ||
|
||
useEffect(() => { | ||
if (!isFetchingNextPage) { | ||
resetIsIntersected(); | ||
} | ||
}, [resetIsIntersected, isFetchingNextPage]); | ||
|
||
useEffect(() => { | ||
if (isFetchingNextPage) return; | ||
if (isIntersected && hasNextPage) { | ||
fetchNextPage(); | ||
} | ||
}, [isIntersected, hasNextPage, isFetchingNextPage, fetchNextPage]); | ||
|
||
const guilds = data?.pages.flatMap((page) => page.items) || []; | ||
|
||
return ( | ||
<section className="grid gap-2"> | ||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> | ||
{isLoading | ||
? Array.from({ length: PAGE_SIZE }, (_, i) => ( | ||
<GuildCardSkeleton key={i} /> | ||
)) | ||
: guilds.map((guild) => <GuildCard key={guild.id} guild={guild} />)} | ||
</div> | ||
<div | ||
ref={useCallback( | ||
(element: HTMLDivElement | null) => { | ||
setIntersection(element); | ||
}, | ||
[setIntersection], | ||
)} | ||
aria-hidden | ||
/> | ||
|
||
{guilds.length === 0 && !isLoading && search ? ( | ||
<p className="mt-6 text-center text-foreground-secondary"> | ||
`No results for "${search}"` | ||
</p> | ||
) : ( | ||
<p className="mt-6 text-center text-foreground-secondary"> | ||
{isFetchingNextPage | ||
? "Loading more..." | ||
: hasNextPage || "No More Data"} | ||
</p> | ||
)} | ||
</section> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
"use client"; | ||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/ToggleGroup"; | ||
import useIsStuck from "@/hooks/useIsStuck"; | ||
import useScrollspy from "@/hooks/useScrollSpy"; | ||
import { cn } from "@/lib/cssUtils"; | ||
import { useAtom, useAtomValue, useSetAtom } from "jotai"; | ||
import { type PropsWithChildren, useEffect } from "react"; | ||
import { activeSectionAtom, isNavStuckAtom, isSearchStuckAtom } from "../atoms"; | ||
import { ACTIVE_SECTION } from "../constants"; | ||
|
||
const Nav = () => { | ||
const isSearchStuck = useAtomValue(isSearchStuckAtom); | ||
const [activeSection, setActiveSection] = useAtom(activeSectionAtom); | ||
const spyActiveSection = useScrollspy(Object.values(ACTIVE_SECTION), 0); | ||
useEffect(() => { | ||
if (!spyActiveSection) return; | ||
setActiveSection( | ||
spyActiveSection as (typeof ACTIVE_SECTION)[keyof typeof ACTIVE_SECTION], | ||
); | ||
}, [spyActiveSection, setActiveSection]); | ||
|
||
return ( | ||
<ToggleGroup | ||
type="single" | ||
className="gap-2" | ||
size={isSearchStuck ? "sm" : "lg"} | ||
variant="secondary" | ||
onValueChange={(value) => | ||
value && | ||
setActiveSection( | ||
value as (typeof ACTIVE_SECTION)[keyof typeof ACTIVE_SECTION], | ||
) | ||
} | ||
value={activeSection} | ||
> | ||
<ToggleGroupItem | ||
value={ACTIVE_SECTION.yourGuilds} | ||
className={cn("rounded-xl transition-all", { | ||
"rounded-lg": isSearchStuck, | ||
})} | ||
asChild | ||
> | ||
<a href={`#${ACTIVE_SECTION.yourGuilds}`}>Your guilds</a> | ||
</ToggleGroupItem> | ||
<ToggleGroupItem | ||
value={ACTIVE_SECTION.exploreGuilds} | ||
className={cn("rounded-xl transition-all", { | ||
"rounded-lg": isSearchStuck, | ||
})} | ||
asChild | ||
> | ||
<a href={`#${ACTIVE_SECTION.exploreGuilds}`}>Explore guilds</a> | ||
</ToggleGroupItem> | ||
</ToggleGroup> | ||
); | ||
}; | ||
|
||
export const StickyNavbar = ({ children }: PropsWithChildren) => { | ||
const setIsNavStuck = useSetAtom(isNavStuckAtom); | ||
const isSearchStuck = useAtomValue(isSearchStuckAtom); | ||
const { ref: navToggleRef } = useIsStuck(setIsNavStuck); | ||
|
||
return ( | ||
<div | ||
className={cn( | ||
"sticky top-0 z-10 flex h-16 w-full items-center transition-all", | ||
{ | ||
"h-12": isSearchStuck, | ||
}, | ||
)} | ||
ref={navToggleRef} | ||
> | ||
<div className="relative flex w-full items-center justify-between"> | ||
<Nav /> | ||
{children} | ||
</div> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.