diff --git a/src/app/_components/AppShell.tsx b/src/app/_components/AppShell.tsx index 3de1fc8..d15c758 100644 --- a/src/app/_components/AppShell.tsx +++ b/src/app/_components/AppShell.tsx @@ -6,6 +6,7 @@ import { Burger, Group, AppShell as MantineAppShell } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; import styles from "./AppShell.module.css"; +import MobileChannelDisplay from "./MobileChannelDisplay"; import Sidebar from "./Sidebar"; type AppShellProps = { @@ -30,7 +31,7 @@ const AppShell: React.FC = ({ children }) => { - digichat + diff --git a/src/app/_components/MobileChannelDisplay.tsx b/src/app/_components/MobileChannelDisplay.tsx new file mode 100644 index 0000000..9360981 --- /dev/null +++ b/src/app/_components/MobileChannelDisplay.tsx @@ -0,0 +1,14 @@ +"use client"; + +import { Text } from "@mantine/core"; + +import { useCurrentChannel } from "#/hooks/useCurrentChannel"; + +/** + * 余計な再描画を防ぐため、チャンネル名を参照する部分を AppShell から分離 + */ +export default function MobileChannelDisplay() { + const { currentChannel } = useCurrentChannel(); + + return {currentChannel && currentChannel.slug}; +} diff --git a/src/app/channels/[channel_id]/_components/Channel/Channel.module.css b/src/app/channels/[channel_id]/_components/Channel/Channel.module.css index a8bf2f3..13d9c5f 100644 --- a/src/app/channels/[channel_id]/_components/Channel/Channel.module.css +++ b/src/app/channels/[channel_id]/_components/Channel/Channel.module.css @@ -1,10 +1,10 @@ .root { + position: relative; display: flex; flex-direction: column; max-width: 100%; height: 100svh; min-height: 100vh; - padding-top: 3rem; } .message-area { @@ -12,4 +12,4 @@ flex-direction: column; height: 100%; overflow-y: scroll; -} \ No newline at end of file +} diff --git a/src/app/channels/[channel_id]/_components/Channel/Channel.tsx b/src/app/channels/[channel_id]/_components/Channel/Channel.tsx index 52b45aa..b251301 100644 --- a/src/app/channels/[channel_id]/_components/Channel/Channel.tsx +++ b/src/app/channels/[channel_id]/_components/Channel/Channel.tsx @@ -5,6 +5,7 @@ import { useLayoutEffect, useRef } from "react"; import { Box } from "@mantine/core"; import ChannelFooter from "../ChannelFooter/ChannelFooter"; +import ChannelHeader from "../ChannelHeader/ChannelHeader"; import styles from "./Channel.module.css"; @@ -38,7 +39,8 @@ const Channel: React.FC = ({ channel_id, messages, user_id }) => { }, [messages]); return ( - + + {messages.map((message) => ( { + const { currentChannel } = useCurrentChannel(); + + return ( + + {currentChannel && currentChannel.slug} + + ); +}; + +export default ChannelHeader; diff --git a/src/app/channels/[channel_id]/_components/CurrentChannelController.tsx b/src/app/channels/[channel_id]/_components/CurrentChannelController.tsx new file mode 100644 index 0000000..961704c --- /dev/null +++ b/src/app/channels/[channel_id]/_components/CurrentChannelController.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { useEffect } from "react"; + +import { Channel } from "@prisma/client"; + +import { useCurrentChannel } from "#/hooks/useCurrentChannel"; + +export default function CurrentChannelController({ + channel, +}: { + channel: Channel | null; +}) { + const { setCurrentChannel } = useCurrentChannel(); + + /** + * チャンネルのページを開いている間、コンテキストにチャンネルの情報を保存する + */ + useEffect(() => { + setCurrentChannel(channel); + + return () => { + setCurrentChannel(null); + }; + }, [channel, setCurrentChannel]); + + return null; +} diff --git a/src/app/channels/[channel_id]/page.tsx b/src/app/channels/[channel_id]/page.tsx index 8586844..4ada866 100644 --- a/src/app/channels/[channel_id]/page.tsx +++ b/src/app/channels/[channel_id]/page.tsx @@ -1,6 +1,8 @@ +import { Metadata } from "next"; import { redirect } from "next/navigation"; import Channel from "./_components/Channel/Channel"; +import CurrentChannelController from "./_components/CurrentChannelController"; import { auth } from "#/libs/auth"; import { prisma } from "#/libs/prisma"; @@ -9,8 +11,9 @@ type ChannelIDPageProps = { params: Promise<{ channel_id: string }>; }; -async function ChannelIDPage({ params }: ChannelIDPageProps) { - const session = await auth(); +export async function generateMetadata({ + params, +}: ChannelIDPageProps): Promise { const { channel_id } = await params; const channel = await prisma.channel.findUnique({ where: { @@ -20,6 +23,22 @@ async function ChannelIDPage({ params }: ChannelIDPageProps) { members: true, }, }); + + if (!channel) return {}; + + return { + title: `${channel.slug} | Digichat`, + }; +} + +async function ChannelIDPage({ params }: ChannelIDPageProps) { + const session = await auth(); + const { channel_id } = await params; + const channel = await prisma.channel.findUnique({ + where: { + id: channel_id, + }, + }); const messages = await prisma.message.findMany({ where: { channelId: channel_id, @@ -41,7 +60,10 @@ async function ChannelIDPage({ params }: ChannelIDPageProps) { const user_id = session.user.id; return ( - + <> + + + ); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d8e69cc..73b447f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -10,6 +10,8 @@ import "@mantine/code-highlight/styles.css"; import "@mantine/core/styles.css"; import AppShell from "./_components/AppShell"; import ChannelsAccordionProvider from "./_components/ChannelsAccordionProvider"; + +import { CurrentChannelProvider } from "#/contexts/CurrentChannelContext"; import "./globals.css"; dayjs.extend(utc); @@ -38,9 +40,11 @@ export default function RootLayout({ children }: RootRayoutProps) { - - {children} - + + + {children} + + diff --git a/src/contexts/CurrentChannelContext.tsx b/src/contexts/CurrentChannelContext.tsx new file mode 100644 index 0000000..f5dc13c --- /dev/null +++ b/src/contexts/CurrentChannelContext.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { createContext, SetStateAction, useState } from "react"; + +import { Channel } from "@prisma/client"; + +export type CurrentChannelContextType = { + currentChannel: Channel | null; + setCurrentChannel: React.Dispatch>; +}; + +export const CurrentChannelContext = + createContext(null); + +export function CurrentChannelProvider({ + children, +}: { + children: React.ReactNode; +}) { + const [currentChannel, setCurrentChannel] = useState(null); + + return ( + + {children} + + ); +} diff --git a/src/hooks/useCurrentChannel.ts b/src/hooks/useCurrentChannel.ts new file mode 100644 index 0000000..22c1fce --- /dev/null +++ b/src/hooks/useCurrentChannel.ts @@ -0,0 +1,20 @@ +"use client"; + +import { useContext } from "react"; + +import { CurrentChannelContext } from "#/contexts/CurrentChannelContext"; + +/** + * 現在開いているチャンネルの情報を利用する + * @returns getter 及び setter + */ +export function useCurrentChannel() { + const context = useContext(CurrentChannelContext); + + if (!context) + throw new Error( + "useCurrentChannel は 内で使用される必要があります" + ); + + return context; +}