From f359542ede3d93b9f30635d4e3e07a7d6a2e7678 Mon Sep 17 00:00:00 2001 From: h8570rg Date: Thu, 1 Aug 2024 22:51:04 +0900 Subject: [PATCH 1/7] fix: maskable --- app/layout.tsx | 2 -- public/manifest.json | 8 +++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 26ed34f..2ebf32d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -25,10 +25,8 @@ export default async function RootLayout({ className={classNames(righteous.variable, mPlus1p.variable, "h-full")} suppressHydrationWarning > - {/* @see https://github.com/shadowwalker/next-pwa */} - diff --git a/public/manifest.json b/public/manifest.json index c9add14..c029c7d 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -10,7 +10,8 @@ { "src": "/icon-192x192.png", "sizes": "192x192", - "type": "image/png" + "type": "image/png", + "purpose": "maskable" }, { "src": "/icon-256x256.png", @@ -24,6 +25,11 @@ "type": "image/png", "purpose": "maskable" }, + { + "src": "/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + }, { "src": "/icon-512x512.png", "sizes": "512x512", From 722df7ab4c6399986a4f1b0fa950cc89832437b1 Mon Sep 17 00:00:00 2001 From: h8570rg Date: Thu, 1 Aug 2024 23:19:44 +0900 Subject: [PATCH 2/7] fix: data modal --- .../matches/(components)/MatchCard/index.tsx | 2 +- .../(components)/DataModal/index.tsx | 44 +++++++++++++++++ .../MatchContextProvider/index.tsx | 11 ++++- .../matches/(routes)/[matchId]/context.ts | 10 ++++ .../matches/(routes)/[matchId]/page.tsx | 5 ++ components/Icon/index.tsx | 6 ++- components/Modal/index.tsx | 48 +++++++++++-------- 7 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx diff --git a/app/(main)/(routes)/matches/(components)/MatchCard/index.tsx b/app/(main)/(routes)/matches/(components)/MatchCard/index.tsx index 2f5a896..16f2cc3 100644 --- a/app/(main)/(routes)/matches/(components)/MatchCard/index.tsx +++ b/app/(main)/(routes)/matches/(components)/MatchCard/index.tsx @@ -20,7 +20,7 @@ export function MatchCard({ match, userId }: { match: Match; userId: string }) {
-

{displayDate}

+

{displayDate}

{match.players.map((player) => ( diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx new file mode 100644 index 0000000..52da7ab --- /dev/null +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { forwardRef } from "react"; +import { + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, +} from "@/components/Modal"; +import { useMatchContext } from "../../context"; + +export function DataModal() { + const { isOpen, onClose } = useMatchContext().dataModal; + + return ( + + + データ + todo + {/* スペーサーとして */} + + + + ); +} + +export const DataModalTrigger = forwardRef< + HTMLButtonElement, + React.ComponentPropsWithoutRef<"button"> +>(function DataModalTrigger({ onClick, ...props }, ref) { + const { onOpen } = useMatchContext().dataModal; + + return ( +
+ @@ -70,6 +74,7 @@ export default async function Match({ +
); diff --git a/components/Icon/index.tsx b/components/Icon/index.tsx index 90abde0..5eed2d8 100644 --- a/components/Icon/index.tsx +++ b/components/Icon/index.tsx @@ -19,7 +19,8 @@ export type IconName = | "chip" | "check-filled" | "edit" - | "description"; + | "description" + | "bar-chart"; export function Icon({ name, @@ -88,6 +89,9 @@ export function IconDefs() { + + + ); diff --git a/components/Modal/index.tsx b/components/Modal/index.tsx index f100a10..e946c9e 100644 --- a/components/Modal/index.tsx +++ b/components/Modal/index.tsx @@ -1,3 +1,8 @@ +"use client"; + +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useCallback } from "react"; + export { Modal, ModalContent, @@ -7,28 +12,29 @@ export { useDisclosure, } from "@nextui-org/react"; +// TODO: 使うか検証中 // パフォーマンスが悪いので使わない -// export const useQueryControlledModal = (key: string) => { -// const searchParams = useSearchParams(); -// const pathname = usePathname(); -// const router = useRouter(); -// const isOpen = searchParams.get(key) === "true"; +export const useQueryControlledModal = (key: string) => { + const searchParams = useSearchParams(); + const pathname = usePathname(); + const router = useRouter(); + const isOpen = searchParams.get(key) === "true"; -// const onOpen = useCallback(() => { -// const params = new URLSearchParams(searchParams); -// params.set(key, "true"); -// router.push(`${pathname}?${params.toString()}`); -// }, [key, pathname, router, searchParams]); + const onOpen = useCallback(() => { + const params = new URLSearchParams(searchParams); + params.set(key, "true"); + router.push(`${pathname}?${params.toString()}`); + }, [key, pathname, router, searchParams]); -// const onClose = useCallback(() => { -// const params = new URLSearchParams(searchParams); -// params.delete(key); -// router.push(`${pathname}?${params.toString()}`); -// }, [key, pathname, router, searchParams]); + const onClose = useCallback(() => { + const params = new URLSearchParams(searchParams); + params.delete(key); + router.push(`${pathname}?${params.toString()}`); + }, [key, pathname, router, searchParams]); -// return { -// isOpen, -// onOpen, -// onClose, -// }; -// }; + return { + isOpen, + onOpen, + onClose, + }; +}; From dc24b5d58df23633514ae753c66df4061a3e5689 Mon Sep 17 00:00:00 2001 From: h8570rg Date: Thu, 1 Aug 2024 23:43:15 +0900 Subject: [PATCH 3/7] fix: middeware --- app/(main)/actions.ts | 7 ++++ app/(main)/layout.tsx | 2 + lib/utils/supabase/middleware.ts | 53 +++++++++++++++++++++++++ lib/utils/supabase/server.ts | 21 ++++++---- middleware.ts | 67 +++----------------------------- 5 files changed, 81 insertions(+), 69 deletions(-) create mode 100644 lib/utils/supabase/middleware.ts diff --git a/app/(main)/actions.ts b/app/(main)/actions.ts index 9a95de4..01e607a 100644 --- a/app/(main)/actions.ts +++ b/app/(main)/actions.ts @@ -15,3 +15,10 @@ export async function signOut() { revalidatePath("/", "layout"); redirect("/login"); } + +/** @see https://github.com/orgs/supabase/discussions/20905 */ +// middlewareの代わりにsessionの更新をする +export async function getUser() { + const supabase = createClient(); + return supabase.auth.getUser(); +} diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx index 8dda2c8..c05f203 100644 --- a/app/(main)/layout.tsx +++ b/app/(main)/layout.tsx @@ -1,10 +1,12 @@ import Navbar from "./(components)/Navbar"; +import { getUser } from "./actions"; export default async function AppLayout({ children, }: { children: React.ReactNode; }) { + await getUser(); // TODO: パフォーマンス検証中。うまくいったらコンテキスト流したい。 return (
diff --git a/lib/utils/supabase/middleware.ts b/lib/utils/supabase/middleware.ts new file mode 100644 index 0000000..3df2ff8 --- /dev/null +++ b/lib/utils/supabase/middleware.ts @@ -0,0 +1,53 @@ +import { createServerClient } from "@supabase/ssr"; +import { type NextRequest, NextResponse } from "next/server"; + +export const updateSession = async (request: NextRequest) => { + // This `try/catch` block is only here for the interactive tutorial. + // Feel free to remove once you have Supabase connected. + try { + // Create an unmodified response + let response = NextResponse.next({ + request: { + headers: request.headers, + }, + }); + + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { + return request.cookies.getAll(); + }, + setAll(cookiesToSet) { + cookiesToSet.forEach(({ name, value }) => + request.cookies.set(name, value), + ); + response = NextResponse.next({ + request, + }); + cookiesToSet.forEach(({ name, value, options }) => + response.cookies.set(name, value, options), + ); + }, + }, + }, + ); + + // This will refresh session if expired - required for Server Components + // https://supabase.com/docs/guides/auth/server-side/nextjs + await supabase.auth.getUser(); + + return response; + } catch (e) { + // If you are here, a Supabase client could not be created! + // This is likely because you have not set up environment variables. + // Check out http://localhost:3000 for Next Steps. + return NextResponse.next({ + request: { + headers: request.headers, + }, + }); + } +}; diff --git a/lib/utils/supabase/server.ts b/lib/utils/supabase/server.ts index edbe8e2..ca9db4c 100644 --- a/lib/utils/supabase/server.ts +++ b/lib/utils/supabase/server.ts @@ -2,7 +2,7 @@ * @see https://supabase.com/docs/guides/auth/server-side/creating-a-client * @see https://supabase.com/docs/guides/auth/server-side/nextjs */ -import { CookieOptions, createServerClient } from "@supabase/ssr"; +import { createServerClient } from "@supabase/ssr"; import { cookies } from "next/headers"; import { Database } from "@/lib/database.types"; @@ -13,14 +13,19 @@ export function createClient() { process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, { cookies: { - get(name: string) { - return cookieStore.get(name)?.value; + getAll() { + return cookieStore.getAll(); }, - set(name: string, value: string, options: CookieOptions) { - cookieStore.set({ name, value, ...options }); - }, - remove(name: string, options: CookieOptions) { - cookieStore.set({ name, value: "", ...options }); + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => { + cookieStore.set(name, value, options); + }); + } catch (error) { + // The `set` method was called from a Server Component. + // This can be ignored if you have middleware refreshing + // user sessions. + } }, }, }, diff --git a/middleware.ts b/middleware.ts index 9a67022..0a33a5e 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,7 +1,6 @@ -import { CookieOptions, createServerClient } from "@supabase/ssr"; -import { NextResponse } from "next/server"; -import type { NextRequest } from "next/server"; +import { NextResponse, type NextRequest } from "next/server"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars const noAuthRoutes = [ "/auth-code-error", "/login", @@ -15,69 +14,15 @@ const noAuthRoutes = [ * @see https://supabase.com/docs/guides/auth/server-side/nextjs */ export async function middleware(request: NextRequest) { - let response = NextResponse.next({ + return NextResponse.next({ request: { headers: request.headers, }, }); - const supabase = createServerClient( - process.env.NEXT_PUBLIC_SUPABASE_URL, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, - { - cookies: { - get(name: string) { - return request.cookies.get(name)?.value; - }, - set(name: string, value: string, options: CookieOptions) { - request.cookies.set({ - name, - value, - ...options, - }); - response = NextResponse.next({ - request: { - headers: request.headers, - }, - }); - response.cookies.set({ - name, - value, - ...options, - }); - }, - remove(name: string, options: CookieOptions) { - request.cookies.set({ - name, - value: "", - ...options, - }); - response = NextResponse.next({ - request: { - headers: request.headers, - }, - }); - response.cookies.set({ - name, - value: "", - ...options, - }); - }, - }, - }, - ); - - const { data, error } = await supabase.auth.getUser(); - - const { pathname } = request.nextUrl; - - if (!noAuthRoutes.includes(pathname)) { - if (error || !data.user) { - return NextResponse.redirect(new URL("/login", request.url)); - } - } - - return response; + // パフォーマンス検証のためコメントアウト + /** @see https://github.com/orgs/supabase/discussions/20905 */ + // return await updateSession(request); } /** From 047a8c4ca563950b07642b0b30e9b657233653a0 Mon Sep 17 00:00:00 2001 From: h8570rg Date: Thu, 1 Aug 2024 23:52:40 +0900 Subject: [PATCH 4/7] fix: pf --- .../matches/(routes)/[matchId]/page.tsx | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/page.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/page.tsx index 3877d51..d6ee48d 100644 --- a/app/(main)/(routes)/matches/(routes)/[matchId]/page.tsx +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/page.tsx @@ -2,8 +2,6 @@ import Link from "next/link"; import { Suspense } from "react"; import { Button } from "@/components/Button"; import { Icon } from "@/components/Icon"; -import { serverServices } from "@/lib/services/server"; -import { dayjs } from "@/lib/utils/date"; import { ChipInputButton } from "./(components)/ChipInputButton"; import { ChipInputModal } from "./(components)/ChipInputModal"; import { DataModal, DataModalTrigger } from "./(components)/DataModal"; @@ -12,39 +10,38 @@ import { MatchContextProvider } from "./(components)/MatchContextProvider"; import { MatchPlayerInputButton } from "./(components)/MatchPlayerInputButton"; import { MatchPlayerInputModal } from "./(components)/MatchPlayerInputModal"; import { MatchTable } from "./(components)/MatchTable"; -import { RuleModal, RuleModalTrigger } from "./(components)/RuleModal"; +import { RuleModalTrigger } from "./(components)/RuleModal"; export default async function Match({ params: { matchId }, }: { params: { matchId: string }; }) { - const { getMatch } = serverServices(); - const match = await getMatch({ matchId }); + // TODO: パフォーマンス検証中 + // const { getMatch } = serverServices(); + // const match = await getMatch({ matchId }); - const { createdAt } = match; + // const { createdAt } = match; - const today = dayjs(); - const targetDate = dayjs(createdAt); - const isSameYear = today.isSame(targetDate, "year"); - const displayDate = isSameYear - ? dayjs(createdAt).format("M/D") - : dayjs(createdAt).format("YYYY/M/D"); + // const today = dayjs(); + // const targetDate = dayjs(createdAt); + // const isSameYear = today.isSame(targetDate, "year"); + // const displayDate = isSameYear + // ? dayjs(createdAt).format("M/D") + // : dayjs(createdAt).format("YYYY/M/D"); // プレイヤー入力モーダルを初期状態で開くかどうか - const isMatchPlayerInputModalDefaultOpen = match.players.length <= 1; + // const isMatchPlayerInputModalDefaultOpen = match.players.length <= 1; return ( - +
-

{displayDate}

+ {/*

{displayDate}

*/}
From 0cdf6c515c871be404df85507eabd662af98c6d9 Mon Sep 17 00:00:00 2001 From: h8570rg Date: Sat, 3 Aug 2024 19:45:42 +0900 Subject: [PATCH 5/7] fix: middleware --- app/(main)/layout.tsx | 4 ++-- middleware.ts | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx index c05f203..26d8c79 100644 --- a/app/(main)/layout.tsx +++ b/app/(main)/layout.tsx @@ -1,12 +1,12 @@ import Navbar from "./(components)/Navbar"; -import { getUser } from "./actions"; +// import { getUser } from "./actions"; export default async function AppLayout({ children, }: { children: React.ReactNode; }) { - await getUser(); // TODO: パフォーマンス検証中。うまくいったらコンテキスト流したい。 + // await getUser(); // TODO: パフォーマンス検証中。うまくいったらコンテキスト流したい。 return (
diff --git a/middleware.ts b/middleware.ts index 0a33a5e..6386f4b 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,4 +1,5 @@ -import { NextResponse, type NextRequest } from "next/server"; +import { type NextRequest } from "next/server"; +import { updateSession } from "./lib/utils/supabase/middleware"; // eslint-disable-next-line @typescript-eslint/no-unused-vars const noAuthRoutes = [ @@ -14,15 +15,15 @@ const noAuthRoutes = [ * @see https://supabase.com/docs/guides/auth/server-side/nextjs */ export async function middleware(request: NextRequest) { - return NextResponse.next({ - request: { - headers: request.headers, - }, - }); + return await updateSession(request); + // return NextResponse.next({ + // request: { + // headers: request.headers, + // }, + // }); // パフォーマンス検証のためコメントアウト /** @see https://github.com/orgs/supabase/discussions/20905 */ - // return await updateSession(request); } /** From 5e45bb46fca9871e2a4495b622c809b7fdf06d2c Mon Sep 17 00:00:00 2001 From: h8570rg Date: Sat, 3 Aug 2024 21:43:06 +0900 Subject: [PATCH 6/7] fix: pf --- .../friends/(components)/AddButton/index.tsx | 7 +- .../(components)/CreateMatchButton/index.tsx | 10 +- .../(components)/ModalController/index.tsx | 6 +- .../(components)/DataModal/index.tsx | 4 +- .../(components)/ModalController/index.tsx | 3 +- .../MatchContextProvider/index.tsx | 16 ++-- .../(components)/Content/index.tsx | 4 +- .../(components)/ModalController/index.tsx | 21 ++++- .../MatchPlayerInputModal/index.tsx | 4 +- .../MatchPlayerInputButton/index.tsx | 4 +- .../RuleModal/(components)/Content/index.tsx | 65 +++++++++++++ .../(components)/ModalController/index.tsx | 14 +++ .../(components)/RuleModal/index.tsx | 94 ++----------------- .../(components)/RuleModalTrigger/index.tsx | 22 +++++ .../matches/(routes)/[matchId]/context.ts | 71 +++++--------- .../matches/(routes)/[matchId]/page.tsx | 26 ++--- app/(main)/actions.ts | 7 -- components/Modal/index.tsx | 35 ++++++- middleware.ts | 12 +-- 19 files changed, 213 insertions(+), 212 deletions(-) create mode 100644 app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/Content/index.tsx create mode 100644 app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/ModalController/index.tsx create mode 100644 app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModalTrigger/index.tsx diff --git a/app/(main)/(routes)/friends/(components)/AddButton/index.tsx b/app/(main)/(routes)/friends/(components)/AddButton/index.tsx index 512d18a..33a8e11 100644 --- a/app/(main)/(routes)/friends/(components)/AddButton/index.tsx +++ b/app/(main)/(routes)/friends/(components)/AddButton/index.tsx @@ -11,7 +11,7 @@ import { ModalBody, ModalContent, ModalHeader, - useDisclosure, + useModal, } from "@/components/Modal"; import { ScrollShadow } from "@/components/ScrollShadow"; import { User } from "@/components/User"; @@ -19,7 +19,7 @@ import { browserServices } from "@/lib/services/browser"; import { addFriends } from "./actions"; export function AddButton() { - const addModal = useDisclosure(); + const addModal = useModal(); const [query, setQuery] = useState(""); const { searchProfiles } = browserServices(); @@ -47,8 +47,7 @@ export function AddButton() { { setQuery(""); diff --git a/app/(main)/(routes)/matches/(components)/CreateMatchButton/index.tsx b/app/(main)/(routes)/matches/(components)/CreateMatchButton/index.tsx index d8f6e91..d1c72e6 100644 --- a/app/(main)/(routes)/matches/(components)/CreateMatchButton/index.tsx +++ b/app/(main)/(routes)/matches/(components)/CreateMatchButton/index.tsx @@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form"; import { Accordion, AccordionItem } from "@/components/Accordion"; import { Button, ButtonGroup } from "@/components/Button"; import { Input } from "@/components/Input"; -import { useDisclosure } from "@/components/Modal"; +import { useModal } from "@/components/Modal"; import { Modal, ModalBody, @@ -63,7 +63,7 @@ const playersCount3DefaultValues: InputSchema = { }; export function CreateMatchButton({ className }: { className?: string }) { - const ruleCreateModal = useDisclosure(); + const ruleCreateModal = useModal(); const [state, formAction] = useFormState(createMatch, {}); const { errors } = state; @@ -87,11 +87,7 @@ export function CreateMatchButton({ className }: { className?: string }) { > ゲームを始める - + {(onClose) => ( <> diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/ChipInputModal/(components)/ModalController/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/ChipInputModal/(components)/ModalController/index.tsx index 0ba5f35..0ba4132 100644 --- a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/ChipInputModal/(components)/ModalController/index.tsx +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/ChipInputModal/(components)/ModalController/index.tsx @@ -11,11 +11,7 @@ export function ChipInputModalController({ const { chipInputModal } = useMatchContext(); return ( - + {children} ); diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx index 52da7ab..a95216e 100644 --- a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/DataModal/index.tsx @@ -11,10 +11,10 @@ import { import { useMatchContext } from "../../context"; export function DataModal() { - const { isOpen, onClose } = useMatchContext().dataModal; + const { dataModal } = useMatchContext(); return ( - + データ todo diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/GameInputModal/(components)/ModalController/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/GameInputModal/(components)/ModalController/index.tsx index 542cb3a..36d1e10 100644 --- a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/GameInputModal/(components)/ModalController/index.tsx +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/GameInputModal/(components)/ModalController/index.tsx @@ -12,8 +12,7 @@ export function GameInputModalController({ return ( diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchContextProvider/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchContextProvider/index.tsx index fe8cd59..dd483c0 100644 --- a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchContextProvider/index.tsx +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchContextProvider/index.tsx @@ -1,22 +1,18 @@ "use client"; -import { useDisclosure, useQueryControlledModal } from "@/components/Modal"; +import { useModal } from "@/components/Modal"; import { MatchContext } from "../../context"; export function MatchContextProvider({ children, - playerInputModalDefaultOpen, }: { children: React.ReactNode; - playerInputModalDefaultOpen: boolean; }) { - const playerInputModal = useDisclosure({ - defaultOpen: playerInputModalDefaultOpen, - }); - const gameInputModal = useDisclosure(); - const chipInputModal = useDisclosure(); - const ruleModal = useDisclosure(); - const dataModal = useQueryControlledModal("data"); // TODO: 使うか検証中 + const playerInputModal = useModal(); + const gameInputModal = useModal(); + const chipInputModal = useModal(); + const ruleModal = useModal(); + const dataModal = useModal(); return ( { + if (isDefaultOpen) { + playerInputModal.onOpen(); + } + // FIXME + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return ( - + {children} ); diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchPlayerInputModal/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchPlayerInputModal/index.tsx index c9f8764..92374fe 100644 --- a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchPlayerInputModal/index.tsx +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/MatchPlayerInputModal/index.tsx @@ -9,8 +9,10 @@ export async function MatchPlayerInputModal({ matchId }: { matchId: string }) { getMatch({ matchId }), ]); + const isDefaultOpen = match.players.length <= 1; + return ( - + , ) { - const gameInputModal = useDisclosure(); + const gameInputModal = useModal(); return ; } diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/Content/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/Content/index.tsx new file mode 100644 index 0000000..a1aaa5d --- /dev/null +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/Content/index.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, +} from "@/components/Modal"; +import { calcMethodLabel, chipRateLabel, rateLabel } from "@/lib/config"; +import { Rule } from "@/lib/type"; + +export function RuleModalContent({ rule }: { rule: Rule }) { + return ( + + ルール + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プレイ人数{rule.playersCount}人
レート{rateLabel[rule.rate]}
チップ{chipRateLabel[rule.chipRate]}
ウマ + {rule.incline.incline1} + {rule.incline.incline2} + {rule.incline.incline3} + {rule.playersCount === 4 && ( + {rule.incline.incline4} + )} +
飛び賞{rule.crackBoxBonus}点
持ち点{rule.defaultPoints}点
オカ{rule.defaultCalcPoints}点
計算{calcMethodLabel[rule.calcMethod]}
+
+ {/* スペーサーとして */} + +
+ ); +} diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/ModalController/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/ModalController/index.tsx new file mode 100644 index 0000000..3b4d02b --- /dev/null +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/(components)/ModalController/index.tsx @@ -0,0 +1,14 @@ +"use client"; + +import { Modal } from "@/components/Modal"; +import { useMatchContext } from "../../../../context"; + +export function ModalController({ children }: { children: React.ReactNode }) { + const { ruleModal } = useMatchContext(); + + return ( + + {children} + + ); +} diff --git a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/index.tsx b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/index.tsx index c591b92..dd45fd2 100644 --- a/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/index.tsx +++ b/app/(main)/(routes)/matches/(routes)/[matchId]/(components)/RuleModal/index.tsx @@ -1,90 +1,14 @@ -"use client"; +import { serverServices } from "@/lib/services/server"; +import { RuleModalContent } from "./(components)/Content"; +import { ModalController } from "./(components)/ModalController"; -import { forwardRef } from "react"; -import { - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalHeader, -} from "@/components/Modal"; -import { calcMethodLabel, chipRateLabel, rateLabel } from "@/lib/config"; -import { Rule } from "@/lib/type"; -import { useMatchContext } from "../../context"; - -export function RuleModal({ rule }: { rule: Rule }) { - const { isOpen, onClose } = useMatchContext().ruleModal; +export async function RuleModal({ matchId }: { matchId: string }) { + const { getMatch } = serverServices(); + const match = await getMatch({ matchId }); return ( - - - ルール - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
プレイ人数{rule.playersCount}人
レート{rateLabel[rule.rate]}
チップ{chipRateLabel[rule.chipRate]}
ウマ - {rule.incline.incline1} - {rule.incline.incline2} - {rule.incline.incline3} - {rule.playersCount === 4 && ( - {rule.incline.incline4} - )} -
飛び賞{rule.crackBoxBonus}点
持ち点{rule.defaultPoints}点
オカ{rule.defaultCalcPoints}点
計算{calcMethodLabel[rule.calcMethod]}
-
- {/* スペーサーとして */} - -
-
+ + + ); } - -export const RuleModalTrigger = forwardRef< - HTMLButtonElement, - React.ComponentPropsWithoutRef<"button"> ->(function RuleModalTrigger({ onClick, ...props }, ref) { - const { onOpen } = useMatchContext().ruleModal; - - return ( - + {/* TODO: fetch */} {/*

{displayDate}

*/}
@@ -70,7 +58,9 @@ export default async function Match({ - {/* */} + + +
diff --git a/app/(main)/actions.ts b/app/(main)/actions.ts index 01e607a..9a95de4 100644 --- a/app/(main)/actions.ts +++ b/app/(main)/actions.ts @@ -15,10 +15,3 @@ export async function signOut() { revalidatePath("/", "layout"); redirect("/login"); } - -/** @see https://github.com/orgs/supabase/discussions/20905 */ -// middlewareの代わりにsessionの更新をする -export async function getUser() { - const supabase = createClient(); - return supabase.auth.getUser(); -} diff --git a/components/Modal/index.tsx b/components/Modal/index.tsx index e946c9e..84c8189 100644 --- a/components/Modal/index.tsx +++ b/components/Modal/index.tsx @@ -1,7 +1,8 @@ "use client"; +import { uniqueId } from "lodash-es"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { useCallback } from "react"; +import { useCallback, useRef } from "react"; export { Modal, @@ -9,15 +10,24 @@ export { ModalHeader, ModalBody, ModalFooter, - useDisclosure, } from "@nextui-org/react"; -// TODO: 使うか検証中 -// パフォーマンスが悪いので使わない -export const useQueryControlledModal = (key: string) => { +export type UseModalReturn = { + isOpen: boolean; + onOpen: () => void; + onClose: () => void; + bind: { + isOpen: boolean; + onOpenChange: (isOpen: boolean) => void; + }; +}; + +export const useModal = (): UseModalReturn => { + const key = useRef(uniqueId("modal")).current; const searchParams = useSearchParams(); const pathname = usePathname(); const router = useRouter(); + const isOpen = searchParams.get(key) === "true"; const onOpen = useCallback(() => { @@ -32,9 +42,24 @@ export const useQueryControlledModal = (key: string) => { router.push(`${pathname}?${params.toString()}`); }, [key, pathname, router, searchParams]); + const onOpenChange = useCallback( + (isOpen: boolean) => { + if (isOpen) { + onOpen(); + } else { + onClose(); + } + }, + [onClose, onOpen], + ); + return { isOpen, onOpen, onClose, + bind: { + isOpen, + onOpenChange, + }, }; }; diff --git a/middleware.ts b/middleware.ts index 6386f4b..fbd5b70 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,13 +1,14 @@ import { type NextRequest } from "next/server"; import { updateSession } from "./lib/utils/supabase/middleware"; +// TODO: 見直し // eslint-disable-next-line @typescript-eslint/no-unused-vars const noAuthRoutes = [ "/auth-code-error", "/login", "/sign-up", "/api/auth/callback", - "/manifest.json", // TODO: 見直し + "/manifest.json", ]; /** @@ -15,15 +16,8 @@ const noAuthRoutes = [ * @see https://supabase.com/docs/guides/auth/server-side/nextjs */ export async function middleware(request: NextRequest) { - return await updateSession(request); - // return NextResponse.next({ - // request: { - // headers: request.headers, - // }, - // }); - - // パフォーマンス検証のためコメントアウト /** @see https://github.com/orgs/supabase/discussions/20905 */ + return await updateSession(request); } /** From 1e13ed63b43ea836c74f7a584d45166a4e380496 Mon Sep 17 00:00:00 2001 From: h8570rg Date: Sat, 3 Aug 2024 22:22:11 +0900 Subject: [PATCH 7/7] fix: middleware --- lib/utils/supabase/middleware.ts | 103 ++++++++++++++++++------------- middleware.ts | 12 +--- 2 files changed, 60 insertions(+), 55 deletions(-) diff --git a/lib/utils/supabase/middleware.ts b/lib/utils/supabase/middleware.ts index 3df2ff8..fa3f374 100644 --- a/lib/utils/supabase/middleware.ts +++ b/lib/utils/supabase/middleware.ts @@ -1,53 +1,68 @@ import { createServerClient } from "@supabase/ssr"; import { type NextRequest, NextResponse } from "next/server"; -export const updateSession = async (request: NextRequest) => { - // This `try/catch` block is only here for the interactive tutorial. - // Feel free to remove once you have Supabase connected. - try { - // Create an unmodified response - let response = NextResponse.next({ - request: { - headers: request.headers, - }, - }); +/** @see https://supabase.com/docs/guides/auth/server-side/nextjs */ +export async function updateSession(request: NextRequest) { + let supabaseResponse = NextResponse.next({ + request, + }); - const supabase = createServerClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, - { - cookies: { - getAll() { - return request.cookies.getAll(); - }, - setAll(cookiesToSet) { - cookiesToSet.forEach(({ name, value }) => - request.cookies.set(name, value), - ); - response = NextResponse.next({ - request, - }); - cookiesToSet.forEach(({ name, value, options }) => - response.cookies.set(name, value, options), - ); - }, + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { + return request.cookies.getAll(); + }, + setAll(cookiesToSet) { + cookiesToSet.forEach(({ name, value }) => + request.cookies.set(name, value), + ); + supabaseResponse = NextResponse.next({ + request, + }); + cookiesToSet.forEach(({ name, value, options }) => + supabaseResponse.cookies.set(name, value, options), + ); }, }, - ); + }, + ); - // This will refresh session if expired - required for Server Components - // https://supabase.com/docs/guides/auth/server-side/nextjs - await supabase.auth.getUser(); + // IMPORTANT: Avoid writing any logic between createServerClient and + // supabase.auth.getUser(). A simple mistake could make it very hard to debug + // issues with users being randomly logged out. - return response; - } catch (e) { - // If you are here, a Supabase client could not be created! - // This is likely because you have not set up environment variables. - // Check out http://localhost:3000 for Next Steps. - return NextResponse.next({ - request: { - headers: request.headers, - }, - }); + const { + data: { user }, + } = await supabase.auth.getUser(); + + if ( + !user && + !request.nextUrl.pathname.startsWith("/login") && + !request.nextUrl.pathname.startsWith("/sign-up") && + !request.nextUrl.pathname.startsWith("/auth-code-error") && + !request.nextUrl.pathname.startsWith("/api/auth") + ) { + // no user, potentially respond by redirecting the user to the login page + const url = request.nextUrl.clone(); + url.pathname = "/login"; + return NextResponse.redirect(url); } -}; + + // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're + // creating a new response object with NextResponse.next() make sure to: + // 1. Pass the request in it, like so: + // const myNewResponse = NextResponse.next({ request }) + // 2. Copy over the cookies, like so: + // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll()) + // 3. Change the myNewResponse object to fit your needs, but avoid changing + // the cookies! + // 4. Finally: + // return myNewResponse + // If this is not done, you may be causing the browser and server to go out + // of sync and terminate the user's session prematurely! + + return supabaseResponse; +} diff --git a/middleware.ts b/middleware.ts index fbd5b70..3376e75 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,16 +1,6 @@ import { type NextRequest } from "next/server"; import { updateSession } from "./lib/utils/supabase/middleware"; -// TODO: 見直し -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const noAuthRoutes = [ - "/auth-code-error", - "/login", - "/sign-up", - "/api/auth/callback", - "/manifest.json", -]; - /** * @see https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=middleware * @see https://supabase.com/docs/guides/auth/server-side/nextjs @@ -33,6 +23,6 @@ export const config = { * - favicon.ico (favicon file) * Feel free to modify this pattern to include more paths. */ - "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", + "/((?!_next/static|_next/image|favicon.ico|manifest.json|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", ], };