Skip to content

Commit

Permalink
Bump to React 19 and Next 15, implement NUQS as also to manage query …
Browse files Browse the repository at this point in the history
…params (#49)

* Enable static rendering for festival and home page

* Bump to Next 15 and React 19

* Update React to stable version
  • Loading branch information
Dosbodoke authored Dec 6, 2024
1 parent 2700235 commit 78a9f23
Show file tree
Hide file tree
Showing 31 changed files with 1,785 additions and 961 deletions.
46 changes: 21 additions & 25 deletions app/[locale]/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import { QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { useSearchParams } from "next/navigation";
import { type AbstractIntlMessages, NextIntlClientProvider } from "next-intl";
import { ThemeProvider } from "next-themes";
import type { ReactNode } from "react";
import { NuqsAdapter } from "nuqs/adapters/next/app";
import { Suspense, type ReactNode } from "react";

import { Toaster } from "@/components/ui/sonner";
import { getQueryClient } from "@/lib/query";
Expand All @@ -18,33 +18,29 @@ interface Props {
}

function Providers({ locale, messages, children }: Props) {
const searchParams = useSearchParams();
const queryClient = getQueryClient();
const now = new Date();

const forcedThemeFromSearchParams =
searchParams.get("view") === "map" ? "light" : undefined;

return (
<QueryClientProvider client={queryClient}>
<NextIntlClientProvider
locale={locale}
messages={messages}
now={now}
timeZone="America/Sao_Paulo"
>
<ThemeProvider
attribute="class"
defaultTheme="system"
forcedTheme={forcedThemeFromSearchParams}
>
<Toaster />
<SpeedInsights />
{children}
</ThemeProvider>
</NextIntlClientProvider>
<ReactQueryDevtools />
</QueryClientProvider>
<Suspense>
<QueryClientProvider client={queryClient}>
<NuqsAdapter>
<NextIntlClientProvider
locale={locale}
messages={messages}
now={now}
timeZone="America/Sao_Paulo"
>
<ThemeProvider attribute="class" defaultTheme="system">
<Toaster />
<SpeedInsights />
{children}
</ThemeProvider>
</NextIntlClientProvider>
</NuqsAdapter>
<ReactQueryDevtools />
</QueryClientProvider>
</Suspense>
);
}

Expand Down
50 changes: 22 additions & 28 deletions app/[locale]/[id]/_components/highline-tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"use client";

import { motion } from "framer-motion";
import { useSearchParams } from "next/navigation";
import { motion } from "motion/react";
import { useTranslations } from "next-intl";
import { useMemo } from "react";
import { useQueryState } from "nuqs";

import type { Highline } from "@/app/actions/getHighline";
import { Ranking } from "@/components/Ranking";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useQueryString } from "@/hooks/useQueryString";

import Comments from "./Comments";
import Info from "./Info";
Expand All @@ -19,38 +17,34 @@ interface Props {

export const HighlineTabs = ({ highline }: Props) => {
const t = useTranslations("highline.tabs");
const searchParams = useSearchParams();
const { replaceQueryParam } = useQueryString();
const [tab, setTab] = useQueryState("tab");

const selectedTab = searchParams.get("tab") || "info";
const selectedTab = tab || "info";

const tabs = useMemo(
() => [
{
id: "info",
label: t("informations.label"),
content: <Info highline={highline} />,
},
{
id: "comments",
label: t("comments"),
content: <Comments highline={highline} />,
},
{
id: "ranking",
label: "Ranking",
content: <Ranking highlines_ids={[highline.id]} />,
},
],
[t, highline]
);
const tabs = [
{
id: "info",
label: t("informations.label"),
content: <Info highline={highline} />,
},
{
id: "comments",
label: t("comments"),
content: <Comments highline={highline} />,
},
{
id: "ranking",
label: "Ranking",
content: <Ranking highlines_ids={[highline.id]} />,
},
];

return (
<>
<Tabs
value={selectedTab}
onValueChange={(value) => {
replaceQueryParam("tab", value);
setTab(value);
}}
>
<TabsList className="my-2 w-full gap-2">
Expand Down
15 changes: 9 additions & 6 deletions app/[locale]/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import HighlineCard from "./_components/HighlineCard";
export const dynamic = "force-dynamic";

type Props = {
params: { id: string };
searchParams: { [key: string]: string | undefined };
params: Promise<{ id: string }>;
searchParams: Promise<{ [key: string]: string | undefined }>;
};

const getHigh = cache(async ({ id }: { id: string }) => {
Expand All @@ -19,10 +19,13 @@ const getHigh = cache(async ({ id }: { id: string }) => {
return result.data;
});

export default async function Highline({
params: { id },
searchParams,
}: Props) {
export default async function Highline(props: Props) {
const params = await props.params;

const {
id
} = params;

const highlines = await getHigh({ id });

if (!highlines || highlines.length === 0) return notFound();
Expand Down
13 changes: 8 additions & 5 deletions app/[locale]/_components/HighlineList.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import { useInfiniteQuery } from "@tanstack/react-query";
import { motion } from "framer-motion";
import { useSearchParams } from "next/navigation";
import { motion } from "motion/react";
import { useQueryState } from "nuqs";

import { getHighline } from "@/app/actions/getHighline";

Expand All @@ -12,13 +12,16 @@ import { HighlineListSkeleton } from "./HighlineListSkeleton";
const PAGE_SIZE = 6;

export function HighlineList() {
const searchParams = useSearchParams();
const searchValue = searchParams.get("q") || "";
const [searchValue = ""] = useQueryState("q");

const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery({
queryKey: ["highlines", { searchValue }],
queryFn: ({ pageParam }) =>
getHighline({ pageParam, searchValue, pageSize: PAGE_SIZE }),
getHighline({
pageParam,
searchValue: searchValue ?? undefined,
pageSize: PAGE_SIZE,
}),
initialPageParam: 1,
getNextPageParam: (lastPage, pages) => {
const nextPage = pages.length + 1;
Expand Down
16 changes: 8 additions & 8 deletions app/[locale]/_components/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

import { SearchIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useQueryState } from "nuqs";

import { Input } from "@/components/ui/input";
import { useQueryString } from "@/hooks/useQueryString";

export default function Search() {
const t = useTranslations("home");
const { searchParams, pushQueryParam, deleteQueryParam } = useQueryString();
const [search, setSearch] = useQueryState("q");

function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const val = e.target as HTMLFormElement;
const search = val.search as HTMLInputElement;
if (search.value) {
pushQueryParam("q", search.value);
const searchInput = val.search as HTMLInputElement;
if (searchInput.value) {
setSearch(searchInput.value);
} else {
deleteQueryParam("q");
setSearch(null);
}
}

Expand All @@ -27,12 +27,12 @@ export default function Search() {
<SearchIcon className="h-6 w-6 text-muted-foreground" />
</span>
<Input
key={searchParams?.get("q")}
key={search}
type="search"
name="search"
placeholder={t("searchPlaceholder")}
autoComplete="off"
defaultValue={searchParams?.get("q") || ""}
defaultValue={search || ""}
className="bg-transparent pl-10 text-base"
/>
</form>
Expand Down
4 changes: 1 addition & 3 deletions app/[locale]/auth/callback/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Refer to the following documentation for more context
// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-sign-in-with-code-exchange

import { cookies } from "next/headers";
import { NextResponse } from "next/server";

import { useSupabaseServer } from "@/utils/supabase/server";
Expand All @@ -16,9 +15,8 @@ export async function GET(request: Request) {
const redirectTo = requestUrl.searchParams.get("redirect_to");

if (code) {
const cookieStore = cookies();
// eslint-disable-next-line react-hooks/rules-of-hooks
const supabase = useSupabaseServer(cookieStore);
const supabase = await useSupabaseServer();

await supabase.auth.exchangeCodeForSession(code);
}
Expand Down
5 changes: 2 additions & 3 deletions app/[locale]/festival/_components/festival-tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { cookies } from "next/headers";
import React from "react";

import { getHighline } from "@/app/actions/getHighline";
Expand All @@ -9,8 +8,8 @@ import { useSupabaseServer } from "@/utils/supabase/server";
import { Highline } from "../../_components/Highline";

export const FestivalTabs = async () => {
const cookieStore = cookies();
const supabase = useSupabaseServer(cookieStore);
// eslint-disable-next-line react-hooks/rules-of-hooks
const supabase = await useSupabaseServer();

const { data: sectors } = await supabase
.from("sector")
Expand Down
21 changes: 13 additions & 8 deletions app/[locale]/festival/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import { Loader2 } from "lucide-react";
import Image from "next/image";
import { useTranslations } from "next-intl";
import { Suspense } from "react";
import { unstable_setRequestLocale } from "next-intl/server";
import { Suspense, use } from "react";

import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";

import { FestivalTabs } from "./_components/festival-tabs";
import { Loader2 } from "lucide-react";

type Props = {
params: { locale: string; username: string };
searchParams: { [key: string]: string | undefined };
params: Promise<{ locale: string; username: string }>;
searchParams: Promise<{ [key: string]: string | undefined }>;
};

export default function Festival({
params: { username },
searchParams,
}: Props) {
export default function Festival(props: Props) {
const params = use(props.params);

const {
locale
} = params;

unstable_setRequestLocale(locale);
const t = useTranslations("festival");

return (
Expand Down
36 changes: 26 additions & 10 deletions app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { GeistSans } from "geist/font/sans";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { useMessages } from "next-intl";
import { unstable_setRequestLocale } from "next-intl/server";
import { use } from "react";

import Footer from "@/components/Footer";
// import Footer from "@/components/Footer";
import NavBar from "@/components/layout/navbar";
import { locales } from "@/navigation";

Expand Down Expand Up @@ -59,21 +61,35 @@ export const metadata: Metadata = {
},
};

export default function RootLayout({
children,
params: { locale },
}: {
children: React.ReactNode;
params: { locale: "en" | "pt" };
}) {
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}

export default function RootLayout(
props: {
children: React.ReactNode;
params: Promise<{ locale: "en" | "pt" }>;
}
) {
const params = use(props.params);

const {
locale
} = params;

const {
children
} = props;

unstable_setRequestLocale(locale);
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale)) notFound();
const messages = useMessages();

return (
// suppressHydrationWarning because of `next-themes`
// refer to https://github.com/pacocoursey/next-themes#with-app
<html lang={locale} suppressHydrationWarning>
(<html lang={locale} suppressHydrationWarning>
<body className={`min-h-screen md:px-0 ${GeistSans.variable} font-sans`}>
<Providers locale={locale} messages={messages}>
<div className="relative flex h-full min-h-screen flex-col">
Expand All @@ -88,6 +104,6 @@ export default function RootLayout({
</Providers>
<Analytics />
</body>
</html>
</html>)
);
}
Loading

0 comments on commit 78a9f23

Please sign in to comment.