From ddd8fb6af3f30b34f47c018b90ca29f947b98698 Mon Sep 17 00:00:00 2001 From: naka-12 <104970808+naka-12@users.noreply.github.com> Date: Wed, 12 Feb 2025 19:22:02 +0900 Subject: [PATCH 1/2] =?UTF-8?q?header/footer=20=E3=82=92=E3=82=B0=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=90=E3=83=AB=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app/chat/layout.tsx | 16 ++- web/app/faq/page.tsx | 66 ++++++------ web/app/friends/layout.tsx | 16 ++- web/app/home/layout.tsx | 16 ++- web/app/layout.tsx | 21 +++- web/app/login/page.tsx | 5 +- web/app/search/layout.tsx | 16 ++- web/app/settings/layout.tsx | 8 +- web/app/signup/page.tsx | 16 ++- web/app/tutorial/layout.tsx | 12 +-- web/components/BottomBar.tsx | 121 ++++++++++++---------- web/components/HeaderFooterContext.tsx | 39 +++++++ web/components/{Header.tsx => TopBar.tsx} | 8 +- web/hooks/useLayoutHeaderFooter.ts | 25 +++++ web/tsconfig.json | 3 +- 15 files changed, 235 insertions(+), 153 deletions(-) create mode 100644 web/components/HeaderFooterContext.tsx rename web/components/{Header.tsx => TopBar.tsx} (84%) create mode 100644 web/hooks/useLayoutHeaderFooter.ts diff --git a/web/app/chat/layout.tsx b/web/app/chat/layout.tsx index a2c62fd9..60d07c64 100644 --- a/web/app/chat/layout.tsx +++ b/web/app/chat/layout.tsx @@ -1,19 +1,17 @@ -import BottomBar from "~/components/BottomBar"; -import Header from "~/components/Header"; +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Layout({ children, }: { children: React.ReactNode; }) { + useSetHeaderFooter({ title: "チャット" }, { activeTab: "3_chat" }); return ( - <> -
- -
{children}
-
- - + +
{children}
+
); } diff --git a/web/app/faq/page.tsx b/web/app/faq/page.tsx index 94b264ca..b471dc8e 100644 --- a/web/app/faq/page.tsx +++ b/web/app/faq/page.tsx @@ -1,47 +1,45 @@ "use client"; -import Header from "~/components/Header"; import TopNavigation from "~/components/common/TopNavigation"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function FAQ() { + useSetHeaderFooter({ title: "よくある質問" }, { activeTab: "4_settings" }); return ( - <> -
-
-
- -
-

- {/* TODO: この辺の構造を直す */} - Q: 東大生以外も利用できますか? -
- A: - 本サービスは東大生のみを対象としています。それゆえ、ECCSアカウントによるログインが必須です。他のGoogleアカウントではログインできません。 -

+
+
+ +
+

+ {/* TODO: この辺の構造を直す */} + Q: 東大生以外も利用できますか? +
+ A: + 本サービスは東大生のみを対象としています。それゆえ、ECCSアカウントによるログインが必須です。他のGoogleアカウントではログインできません。 +

-

- Q: 授業登録機能はすべての学部に対応していますか? -
- A: - 本サービスの授業登録機能は前期教養学部のみに対応しており、今のところ後期学部には対応しておりません。 -

+

+ Q: 授業登録機能はすべての学部に対応していますか? +
+ A: + 本サービスの授業登録機能は前期教養学部のみに対応しており、今のところ後期学部には対応しておりません。 +

-

- Q: 収集された個人情報はどのように利用されますか? -
- A: - 収集した個人情報は、サインインおよびサービス提供の目的にのみ使用され、他の目的には使用されません。 -

+

+ Q: 収集された個人情報はどのように利用されますか? +
+ A: + 収集した個人情報は、サインインおよびサービス提供の目的にのみ使用され、他の目的には使用されません。 +

-

- Q: 東大公式のアプリですか? -
- A: - 本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません -

-
+

+ Q: 東大公式のアプリですか? +
+ A: + 本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません +

- +
); } diff --git a/web/app/friends/layout.tsx b/web/app/friends/layout.tsx index b5295993..81206497 100644 --- a/web/app/friends/layout.tsx +++ b/web/app/friends/layout.tsx @@ -1,19 +1,17 @@ -import BottomBar from "~/components/BottomBar"; -import Header from "~/components/Header"; +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Layout({ children, }: { children: React.ReactNode; }) { + useSetHeaderFooter({ title: "フレンド" }, { activeTab: "1_friends" }); return ( - <> -
- -
{children}
-
- - + +
{children}
+
); } diff --git a/web/app/home/layout.tsx b/web/app/home/layout.tsx index ec50a110..420f80c9 100644 --- a/web/app/home/layout.tsx +++ b/web/app/home/layout.tsx @@ -1,19 +1,17 @@ -import BottomBar from "~/components/BottomBar"; -import Header from "~/components/Header"; +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Layout({ children, }: { children: React.ReactNode; }) { + useSetHeaderFooter({}, { activeTab: "0_home" }); return ( - <> -
- -
{children}
-
- - + +
{children}
+
); } diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 254ffcf8..afb214c4 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -9,6 +9,12 @@ import "@fontsource/roboto/400.css"; import "@fontsource/roboto/500.css"; import "@fontsource/roboto/700.css"; import BanLandscape from "~/components/BanLandscape"; +import BottomBar from "~/components/BottomBar"; +import { + HeaderFooterProvider, + useHeaderFooterInternal, +} from "~/components/HeaderFooterContext"; +import TopBar from "~/components/TopBar"; import { AlertProvider } from "~/components/common/alert/AlertProvider"; import { ModalProvider } from "~/components/common/modal/ModalProvider"; import AuthProvider from "~/firebase/auth/AuthProvider"; @@ -50,7 +56,9 @@ export default function RootLayout({ - {children} + + {children} + @@ -61,3 +69,14 @@ export default function RootLayout({ ); } + +function MainLayout({ children }: { children: React.ReactNode }) { + const { headerProps, bottomBarProps } = useHeaderFooterInternal(); + return ( + <> + + {children} + + + ); +} diff --git a/web/app/login/page.tsx b/web/app/login/page.tsx index 98c2c0de..3975d6cb 100644 --- a/web/app/login/page.tsx +++ b/web/app/login/page.tsx @@ -6,13 +6,13 @@ import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import * as user from "~/api/user"; import { getByGUID } from "~/api/user"; -import Header from "~/components/Header"; import { auth } from "~/firebase/config"; import "./style.css"; import { useState } from "react"; import { CourseMateIcon } from "~/components/common/CourseMateIcon"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; const provider = new GoogleAuthProvider(); const ALLOW_ANY_MAIL_ADDR = @@ -125,9 +125,10 @@ export default function Login() { return ; } + useSetHeaderFooter({ info: true }, { activeTab: "none" }); + return ( -
diff --git a/web/app/search/layout.tsx b/web/app/search/layout.tsx index 3f75b04d..c88d7544 100644 --- a/web/app/search/layout.tsx +++ b/web/app/search/layout.tsx @@ -1,19 +1,17 @@ -import BottomBar from "~/components/BottomBar"; -import Header from "~/components/Header"; +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Layout({ children, }: { children: React.ReactNode; }) { + useSetHeaderFooter({ title: "検索" }, { activeTab: "2_search" }); return ( - <> -
- -
{children}
-
- - + +
{children}
+
); } diff --git a/web/app/settings/layout.tsx b/web/app/settings/layout.tsx index 0dfbe9fc..f966f2ab 100644 --- a/web/app/settings/layout.tsx +++ b/web/app/settings/layout.tsx @@ -1,19 +1,19 @@ -import BottomBar from "~/components/BottomBar"; -import Header from "~/components/Header"; +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Layout({ children, }: { children: React.ReactNode; }) { + useSetHeaderFooter({ title: "設定" }, { activeTab: "4_settings" }); return ( <> -
{children}
- ); } diff --git a/web/app/signup/page.tsx b/web/app/signup/page.tsx index 99b3e3d9..a40deeb0 100644 --- a/web/app/signup/page.tsx +++ b/web/app/signup/page.tsx @@ -5,8 +5,8 @@ import { useState } from "react"; import type { Step1User } from "common/zod/types"; import { useRouter } from "next/navigation"; -import Header from "~/components/Header"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; import { register } from "./functions"; import Step1 from "./steps/step1_profile"; import Step2, { type Step2Data } from "./steps/step2_img"; @@ -78,14 +78,12 @@ function Registration() { } } export default function RegistrationPage() { + useSetHeaderFooter({ title: "登録" }, { activeTab: "none" }); return ( - <> -
- -
- -
-
- + +
+ +
+
); } diff --git a/web/app/tutorial/layout.tsx b/web/app/tutorial/layout.tsx index 1e86f9cb..2826fccf 100644 --- a/web/app/tutorial/layout.tsx +++ b/web/app/tutorial/layout.tsx @@ -1,17 +1,15 @@ -import Header from "~/components/Header"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Layout({ children, }: { children: React.ReactNode; }) { + useSetHeaderFooter({ title: "チュートリアル" }, { activeTab: "none" }); return ( - <> -
- -
{children}
-
- + +
{children}
+
); } diff --git a/web/components/BottomBar.tsx b/web/components/BottomBar.tsx index b644eb15..dc6fd4f7 100644 --- a/web/components/BottomBar.tsx +++ b/web/components/BottomBar.tsx @@ -12,8 +12,14 @@ import { MdChat } from "react-icons/md"; import { MdSettings } from "react-icons/md"; import { MdOutlineHome } from "react-icons/md"; -type Props = { - activeTab: "0_home" | "1_friends" | "2_search" | "3_chat" | "4_settings"; +export type BottomBarProps = { + activeTab: + | "0_home" + | "1_friends" + | "2_search" + | "3_chat" + | "4_settings" + | "none"; }; function BottomBarCell({ @@ -33,60 +39,63 @@ function BottomBarCell({ ); } -export default function BottomBar(props: Props) { - const { activeTab } = props; +export default function BottomBar(props: BottomBarProps) { return ( -
- - ) : ( - - ) - } - /> - - ) : ( - - ) - } - /> - - ) : ( - - ) - } - /> - - ) : ( - - ) - } - /> - - ) : ( - - ) - } - /> -
+ <> + {props.activeTab !== "none" && ( +
+ + ) : ( + + ) + } + /> + + ) : ( + + ) + } + /> + + ) : ( + + ) + } + /> + + ) : ( + + ) + } + /> + + ) : ( + + ) + } + /> +
+ )} + ); } diff --git a/web/components/HeaderFooterContext.tsx b/web/components/HeaderFooterContext.tsx new file mode 100644 index 00000000..2680c0a9 --- /dev/null +++ b/web/components/HeaderFooterContext.tsx @@ -0,0 +1,39 @@ +import { type ReactNode, createContext, useContext, useState } from "react"; +import type { BottomBarProps } from "./BottomBar"; +import type { TopBarProps } from "./TopBar"; + +interface HeaderFooterContextType { + headerProps: TopBarProps; + setHeaderProps: (props: TopBarProps) => void; + bottomBarProps: BottomBarProps; + setBottomBarProps: (props: BottomBarProps) => void; +} + +const HeaderFooterContext = createContext( + undefined, +); + +export const HeaderFooterProvider = ({ children }: { children: ReactNode }) => { + const [headerProps, setHeaderProps] = useState({}); + const [bottomBarProps, setBottomBarProps] = useState({ + activeTab: "0_home", + }); + + return ( + + {children} + + ); +}; + +export const useHeaderFooterInternal = () => { + const context = useContext(HeaderFooterContext); + if (!context) { + throw new Error( + "useHeaderFooter must be used within a HeaderFooterProvider", + ); + } + return context; +}; diff --git a/web/components/Header.tsx b/web/components/TopBar.tsx similarity index 84% rename from web/components/Header.tsx rename to web/components/TopBar.tsx index f66d3714..eefbaa68 100644 --- a/web/components/Header.tsx +++ b/web/components/TopBar.tsx @@ -2,13 +2,15 @@ import Link from "next/link"; import { MdInfoOutline } from "react-icons/md"; import { CourseMateIcon } from "./common/CourseMateIcon"; -type Props = { +export type TopBarProps = { title?: string; info?: boolean; + useBackButton?: boolean; }; -export default function Header(props: Props) { - const { title, info } = props; +export default function TopBar(props: TopBarProps) { + const { title, info, useBackButton } = props; + // TODO: Implement back button return (
{title && ( diff --git a/web/hooks/useLayoutHeaderFooter.ts b/web/hooks/useLayoutHeaderFooter.ts new file mode 100644 index 00000000..d1b01232 --- /dev/null +++ b/web/hooks/useLayoutHeaderFooter.ts @@ -0,0 +1,25 @@ +"use client"; + +import { useLayoutEffect } from "react"; +import type { BottomBarProps } from "~/components/BottomBar"; +import type { TopBarProps } from "~/components/TopBar"; +import { useHeaderFooterInternal } from "../components/HeaderFooterContext"; + +export function useSetHeaderFooter( + { title, info, useBackButton }: TopBarProps, + { activeTab }: BottomBarProps, +) { + const { setHeaderProps, setBottomBarProps } = useHeaderFooterInternal(); + + useLayoutEffect(() => { + setHeaderProps({ title, info, useBackButton }); + setBottomBarProps({ activeTab }); + }, [ + title, + info, + useBackButton, + activeTab, + setHeaderProps, + setBottomBarProps, + ]); +} diff --git a/web/tsconfig.json b/web/tsconfig.json index dfe5a684..6b49e944 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -36,7 +36,8 @@ ".next/types/**/*.ts", "common", "app", - "components" + "components", + "hooks" ], "exclude": ["./node_modules"] } From 2cca58698c8aa5554cfa7d9ebc4283b32d304b3c Mon Sep 17 00:00:00 2001 From: naka-12 <104970808+naka-12@users.noreply.github.com> Date: Wed, 12 Feb 2025 19:54:02 +0900 Subject: [PATCH 2/2] =?UTF-8?q?settings=20=E3=81=AE=20topnavigation=20?= =?UTF-8?q?=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app/faq/page.tsx | 4 +-- web/app/settings/aboutUs/page.tsx | 9 ++++-- web/app/settings/card/page.tsx | 8 ++++-- web/app/settings/contact/page.tsx | 9 ++++-- web/app/settings/courses/page.tsx | 13 ++++----- web/app/settings/delete/page.tsx | 8 ++++-- web/app/settings/disclaimer/page.tsx | 9 ++++-- web/app/settings/interests/page.tsx | 3 ++ web/app/settings/layout.tsx | 2 -- web/app/settings/notification/page.tsx | 10 +++++-- web/app/settings/page.tsx | 4 +++ web/app/settings/profile/page.tsx | 3 ++ web/app/tutorial/layout.tsx | 2 ++ web/components/TopBar.tsx | 35 ++++++++++++++--------- web/components/common/TopNavigation.tsx | 37 ------------------------- web/hooks/useLayoutHeaderFooter.ts | 4 +-- 16 files changed, 83 insertions(+), 77 deletions(-) delete mode 100644 web/components/common/TopNavigation.tsx diff --git a/web/app/faq/page.tsx b/web/app/faq/page.tsx index b471dc8e..a1e373e9 100644 --- a/web/app/faq/page.tsx +++ b/web/app/faq/page.tsx @@ -1,14 +1,12 @@ "use client"; -import TopNavigation from "~/components/common/TopNavigation"; import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function FAQ() { - useSetHeaderFooter({ title: "よくある質問" }, { activeTab: "4_settings" }); + useSetHeaderFooter({ title: "よくある質問" }, { activeTab: "none" }); return (
-

{/* TODO: この辺の構造を直す */} diff --git a/web/app/settings/aboutUs/page.tsx b/web/app/settings/aboutUs/page.tsx index 97293496..1ecac30c 100644 --- a/web/app/settings/aboutUs/page.tsx +++ b/web/app/settings/aboutUs/page.tsx @@ -1,13 +1,18 @@ +"use client"; + import { FaGithub, FaXTwitter } from "react-icons/fa6"; import { MdLanguage } from "react-icons/md"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; -import TopNavigation from "~/components/common/TopNavigation"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function AboutUs() { + useSetHeaderFooter( + { title: "About Us", backButtonPath: "/settings" }, + { activeTab: "4_settings" }, + ); return (

-

CourseMateについて diff --git a/web/app/settings/card/page.tsx b/web/app/settings/card/page.tsx index 888c6df6..22504786 100644 --- a/web/app/settings/card/page.tsx +++ b/web/app/settings/card/page.tsx @@ -7,7 +7,7 @@ import { useAboutMe } from "~/api/user"; import { Card } from "~/components/Card"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; -import TopNavigation from "~/components/common/TopNavigation"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function SettingsProfile() { const { state } = useAboutMe(); @@ -18,6 +18,11 @@ export default function SettingsProfile() { if (error) throw error; + useSetHeaderFooter( + { title: "カードのプレビュー", backButtonPath: "/settings" }, + { activeTab: "4_settings" }, + ); + return ( {loading ? ( @@ -26,7 +31,6 @@ export default function SettingsProfile() {

データがありません。

) : (
-
-

ご利用いただきありがとうございます。サービスに関するご意見やバグ報告がございましたら、以下のリンクからお問い合わせください。皆様のフィードバックは、サービスの改善に役立てさせていただきます。 diff --git a/web/app/settings/courses/page.tsx b/web/app/settings/courses/page.tsx index b86b3e86..fc055c2c 100644 --- a/web/app/settings/courses/page.tsx +++ b/web/app/settings/courses/page.tsx @@ -1,9 +1,9 @@ "use client"; -import Link from "next/link"; import { useAboutMe } from "~/api/user"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; import EditableCoursesTable from "~/components/course/EditableCoursesTable"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function EditCourses() { const { state } = useAboutMe(); @@ -13,9 +13,13 @@ export default function EditCourses() { if (error) throw error; + useSetHeaderFooter( + { title: "授業", backButtonPath: "/settings" }, + { activeTab: "4_settings" }, + ); + return (

-

授業編集

{loading ? ( ) : data ? ( @@ -25,11 +29,6 @@ export default function EditCourses() { ) : (

データがありません。

)} -
- - 設定画面に戻る - -
); } diff --git a/web/app/settings/delete/page.tsx b/web/app/settings/delete/page.tsx index d5245d82..ece46a84 100644 --- a/web/app/settings/delete/page.tsx +++ b/web/app/settings/delete/page.tsx @@ -4,8 +4,8 @@ import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback } from "react"; import { deleteAccount } from "~/api/user"; -import TopNavigation from "~/components/common/TopNavigation"; import { useAlert } from "~/components/common/alert/AlertProvider"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function DeleteAccount() { const router = useRouter(); @@ -33,9 +33,13 @@ export default function DeleteAccount() { }); }, [showAlert, enqueueSnackbar, router.push]); + useSetHeaderFooter( + { title: "アカウント削除", backButtonPath: "/settings" }, + { activeTab: "4_settings" }, + ); + return (
-

アカウントを削除した場合、マッチングやチャットに関する情報の一切が削除されます。 diff --git a/web/app/settings/disclaimer/page.tsx b/web/app/settings/disclaimer/page.tsx index 1c18f180..80fa4d68 100644 --- a/web/app/settings/disclaimer/page.tsx +++ b/web/app/settings/disclaimer/page.tsx @@ -1,11 +1,16 @@ +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; -import TopNavigation from "~/components/common/TopNavigation"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Disclaimer() { + useSetHeaderFooter( + { title: "免責事項", backButtonPath: "/settings" }, + { activeTab: "4_settings" }, + ); return (

-

本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません。本サービスは東大生のみを対象としており、ECCSアカウントによるログインが必須です。 diff --git a/web/app/settings/interests/page.tsx b/web/app/settings/interests/page.tsx index eb857c21..cc56575c 100644 --- a/web/app/settings/interests/page.tsx +++ b/web/app/settings/interests/page.tsx @@ -7,6 +7,7 @@ import { useEffect, useState } from "react"; import { MdAdd, MdClose } from "react-icons/md"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; import { useAlert } from "~/components/common/alert/AlertProvider"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; import * as subject from "../../../api/subject"; export default function EditInterest() { @@ -77,6 +78,8 @@ export default function EditInterest() { if (error) throw error; + useSetHeaderFooter({ title: "興味分野の編集" }, { activeTab: "4_settings" }); + return loading ? ( ) : !data ? ( diff --git a/web/app/settings/layout.tsx b/web/app/settings/layout.tsx index f966f2ab..d662d8e5 100644 --- a/web/app/settings/layout.tsx +++ b/web/app/settings/layout.tsx @@ -1,14 +1,12 @@ "use client"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; -import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; export default function Layout({ children, }: { children: React.ReactNode; }) { - useSetHeaderFooter({ title: "設定" }, { activeTab: "4_settings" }); return ( <> diff --git a/web/app/settings/notification/page.tsx b/web/app/settings/notification/page.tsx index 7afda68c..6a68c954 100644 --- a/web/app/settings/notification/page.tsx +++ b/web/app/settings/notification/page.tsx @@ -1,5 +1,7 @@ +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; -import TopNavigation from "~/components/common/TopNavigation"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; // Notification型を定義 type Notification = { @@ -33,11 +35,13 @@ const sortedNotifications = notifications.sort( ); export default function Notification() { + useSetHeaderFooter( + { title: "運営からのお知らせ", backButtonPath: "/settings" }, + { activeTab: "4_settings" }, + ); return (

- -
    {sortedNotifications.map((notification) => (
  • diff --git a/web/app/settings/page.tsx b/web/app/settings/page.tsx index 646e009b..9e9520f5 100644 --- a/web/app/settings/page.tsx +++ b/web/app/settings/page.tsx @@ -1,6 +1,9 @@ +"use client"; + import Link from "next/link"; import { MdChevronRight } from "react-icons/md"; import LogOutButton from "~/components/LogOutButton"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; function Item({ href, @@ -22,6 +25,7 @@ function Item({ } export default function Settings() { + useSetHeaderFooter({ title: "設定" }, { activeTab: "4_settings" }); return (

    基本情報

    diff --git a/web/app/settings/profile/page.tsx b/web/app/settings/profile/page.tsx index b89d7873..72de255d 100644 --- a/web/app/settings/profile/page.tsx +++ b/web/app/settings/profile/page.tsx @@ -14,6 +14,7 @@ import { useAlert } from "~/components/common/alert/AlertProvider"; import PhotoModal from "~/components/config/PhotoModal"; import { PhotoPreviewButton } from "~/components/config/PhotoPreview"; import UserAvatar from "~/components/human/avatar"; +import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; const faculties = Object.keys(facultiesAndDepartments); @@ -94,6 +95,8 @@ function EditProfile({ defaultValues }: { defaultValues: User }) { const [selectedFaculty, setSelectedFaculty] = useState(values.faculty); const departments = facultiesAndDepartments[selectedFaculty] ?? null; + useSetHeaderFooter({ title: "プロフィール" }, { activeTab: "4_settings" }); + return (
    diff --git a/web/app/tutorial/layout.tsx b/web/app/tutorial/layout.tsx index 2826fccf..12af165f 100644 --- a/web/app/tutorial/layout.tsx +++ b/web/app/tutorial/layout.tsx @@ -1,3 +1,5 @@ +"use client"; + import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import { useSetHeaderFooter } from "~/hooks/useLayoutHeaderFooter"; diff --git a/web/components/TopBar.tsx b/web/components/TopBar.tsx index eefbaa68..ed16cb03 100644 --- a/web/components/TopBar.tsx +++ b/web/components/TopBar.tsx @@ -1,27 +1,36 @@ import Link from "next/link"; -import { MdInfoOutline } from "react-icons/md"; +import { MdArrowBackIosNew, MdInfoOutline } from "react-icons/md"; import { CourseMateIcon } from "./common/CourseMateIcon"; export type TopBarProps = { title?: string; info?: boolean; - useBackButton?: boolean; + backButtonPath?: string; }; export default function TopBar(props: TopBarProps) { - const { title, info, useBackButton } = props; - // TODO: Implement back button + const { title, info, backButtonPath } = props; + return (
    - {title && ( - - - - )} + {title && + (!backButtonPath ? ( + + + + ) : ( + + + + ))} {title ? (

    diff --git a/web/components/common/TopNavigation.tsx b/web/components/common/TopNavigation.tsx deleted file mode 100644 index 71894f4f..00000000 --- a/web/components/common/TopNavigation.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Link from "next/link"; -import { MdOutlineArrowBack } from "react-icons/md"; - -type TopNavigationProps = { - title: string; -}; - -/** - * Settings の子ページから Setting に戻るナビゲーションを提供 - */ -export default function TopNavigation({ title }: TopNavigationProps) { - return ( -
    - - {/* biome-ignore lint/a11y/useButtonType: */} - - -

    - {title} -

    -
    - ); -} diff --git a/web/hooks/useLayoutHeaderFooter.ts b/web/hooks/useLayoutHeaderFooter.ts index d1b01232..e714126b 100644 --- a/web/hooks/useLayoutHeaderFooter.ts +++ b/web/hooks/useLayoutHeaderFooter.ts @@ -6,13 +6,13 @@ import type { TopBarProps } from "~/components/TopBar"; import { useHeaderFooterInternal } from "../components/HeaderFooterContext"; export function useSetHeaderFooter( - { title, info, useBackButton }: TopBarProps, + { title, info, backButtonPath: useBackButton }: TopBarProps, { activeTab }: BottomBarProps, ) { const { setHeaderProps, setBottomBarProps } = useHeaderFooterInternal(); useLayoutEffect(() => { - setHeaderProps({ title, info, useBackButton }); + setHeaderProps({ title, info, backButtonPath: useBackButton }); setBottomBarProps({ activeTab }); }, [ title,