diff --git a/frontend/web/src/components/ShortcutActionsDropdown.tsx b/frontend/web/src/components/ShortcutActionsDropdown.tsx index d262fcf..57d23d7 100644 --- a/frontend/web/src/components/ShortcutActionsDropdown.tsx +++ b/frontend/web/src/components/ShortcutActionsDropdown.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { useNavigate } from "react-router-dom"; +import useNavigateTo from "@/hooks/useNavigateTo"; import { shortcutService } from "../services"; import useUserStore from "../stores/v1/user"; import { showCommonDialog } from "./Alert"; @@ -16,7 +16,7 @@ interface Props { const ShortcutActionsDropdown = (props: Props) => { const { shortcut } = props; const { t } = useTranslation(); - const navigate = useNavigate(); + const navigateTo = useNavigateTo(); const currentUser = useUserStore().getCurrentUser(); const [showEditDialog, setShowEditDialog] = useState(false); const [showQRCodeDialog, setShowQRCodeDialog] = useState(false); @@ -34,7 +34,7 @@ const ShortcutActionsDropdown = (props: Props) => { }; const gotoAnalytics = () => { - navigate(`/shortcut/${shortcut.id}#analytics`); + navigateTo(`/shortcut/${shortcut.id}#analytics`); }; return ( diff --git a/frontend/web/src/components/common/Dropdown.tsx b/frontend/web/src/components/common/Dropdown.tsx index 7d523ed..1caedb8 100644 --- a/frontend/web/src/components/common/Dropdown.tsx +++ b/frontend/web/src/components/common/Dropdown.tsx @@ -1,5 +1,4 @@ -import { ReactNode, useEffect, useRef } from "react"; -import useToggle from "../../hooks/useToggle"; +import { ReactNode, useEffect, useRef, useState } from "react"; import Icon from "../Icon"; interface Props { @@ -11,14 +10,14 @@ interface Props { const Dropdown: React.FC = (props: Props) => { const { trigger, actions, className, actionsClassName } = props; - const [dropdownStatus, toggleDropdownStatus] = useToggle(false); + const [dropdownStatus, setDropdownStatus] = useState(false); const dropdownWrapperRef = useRef(null); useEffect(() => { if (dropdownStatus) { const handleClickOutside = (event: MouseEvent) => { if (!dropdownWrapperRef.current?.contains(event.target as Node)) { - toggleDropdownStatus(false); + setDropdownStatus(false); } }; @@ -35,7 +34,7 @@ const Dropdown: React.FC = (props: Props) => { const handleToggleDropdownStatus = (e: React.MouseEvent) => { e.stopPropagation(); - toggleDropdownStatus(); + setDropdownStatus(!dropdownStatus); }; return ( diff --git a/frontend/web/src/hooks/useNavigateTo.ts b/frontend/web/src/hooks/useNavigateTo.ts new file mode 100644 index 0000000..ad743cd --- /dev/null +++ b/frontend/web/src/hooks/useNavigateTo.ts @@ -0,0 +1,20 @@ +import { NavigateOptions, useNavigate } from "react-router-dom"; + +const useNavigateTo = () => { + const navigateTo = useNavigate(); + + const navigateToWithViewTransition = (to: string, options?: NavigateOptions) => { + const document = window.document as any; + if (!document.startViewTransition) { + navigateTo(to, options); + } else { + document.startViewTransition(() => { + navigateTo(to, options); + }); + } + }; + + return navigateToWithViewTransition; +}; + +export default useNavigateTo; diff --git a/frontend/web/src/hooks/useToggle.ts b/frontend/web/src/hooks/useToggle.ts deleted file mode 100644 index fb5e680..0000000 --- a/frontend/web/src/hooks/useToggle.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useCallback, useState } from "react"; - -// Parameter is the boolean, with default "false" value -const useToggle = (initialState = false): [boolean, (nextState?: boolean) => void] => { - // Initialize the state - const [state, setState] = useState(initialState); - - // Define and memorize toggler function in case we pass down the comopnent, - // This function change the boolean value to it's opposite value - const toggle = useCallback((nextState?: boolean) => { - if (nextState !== undefined) { - setState(nextState); - } else { - setState((state) => !state); - } - }, []); - - return [state, toggle]; -}; - -export default useToggle; diff --git a/frontend/web/src/layouts/Root.tsx b/frontend/web/src/layouts/Root.tsx index c0ca6de..80a1b38 100644 --- a/frontend/web/src/layouts/Root.tsx +++ b/frontend/web/src/layouts/Root.tsx @@ -2,13 +2,14 @@ import { useColorScheme } from "@mui/joy"; import { isEqual } from "lodash-es"; import { useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { Outlet, useNavigate } from "react-router-dom"; +import { Outlet } from "react-router-dom"; +import useNavigateTo from "@/hooks/useNavigateTo"; import { UserSetting_ColorTheme } from "@/types/proto/api/v2/user_setting_service"; import Header from "../components/Header"; import useUserStore from "../stores/v1/user"; const Root: React.FC = () => { - const navigate = useNavigate(); + const navigateTo = useNavigateTo(); const { setMode } = useColorScheme(); const { i18n } = useTranslation(); const userStore = useUserStore(); @@ -18,7 +19,7 @@ const Root: React.FC = () => { useEffect(() => { if (!currentUser) { - navigate("/auth", { + navigateTo("/auth", { replace: true, }); return; diff --git a/frontend/web/src/pages/Home.tsx b/frontend/web/src/pages/Home.tsx index 97b3801..a965783 100644 --- a/frontend/web/src/pages/Home.tsx +++ b/frontend/web/src/pages/Home.tsx @@ -69,7 +69,7 @@ const Home: React.FC = () => { {loadingState.isLoading ? ( -
+
loading
diff --git a/frontend/web/src/pages/ShortcutDetail.tsx b/frontend/web/src/pages/ShortcutDetail.tsx index b4028ea..49e0bf0 100644 --- a/frontend/web/src/pages/ShortcutDetail.tsx +++ b/frontend/web/src/pages/ShortcutDetail.tsx @@ -4,7 +4,8 @@ import copy from "copy-to-clipboard"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; -import { useLoaderData, useNavigate } from "react-router-dom"; +import { useLoaderData } from "react-router-dom"; +import useNavigateTo from "@/hooks/useNavigateTo"; import { showCommonDialog } from "../components/Alert"; import AnalyticsView from "../components/AnalyticsView"; import CreateShortcutDialog from "../components/CreateShortcutDialog"; @@ -23,7 +24,7 @@ interface State { const ShortcutDetail = () => { const { t } = useTranslation(); - const navigate = useNavigate(); + const navigateTo = useNavigateTo(); const shortcutId = (useLoaderData() as Shortcut).id; const shortcut = shortcutService.getShortcutById(shortcutId) as Shortcut; const currentUser = useUserStore().getCurrentUser(); @@ -56,7 +57,7 @@ const ShortcutDetail = () => { style: "danger", onConfirm: async () => { await shortcutService.deleteShortcutById(shortcut.id); - navigate("/", { + navigateTo("/", { replace: true, }); }, diff --git a/frontend/web/src/pages/SignIn.tsx b/frontend/web/src/pages/SignIn.tsx index 07cf059..ebed857 100644 --- a/frontend/web/src/pages/SignIn.tsx +++ b/frontend/web/src/pages/SignIn.tsx @@ -2,7 +2,8 @@ import { Button, Input } from "@mui/joy"; import React, { FormEvent, useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; -import { Link, useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; +import useNavigateTo from "@/hooks/useNavigateTo"; import useWorkspaceStore from "@/stores/v1/workspace"; import * as api from "../helpers/api"; import useLoading from "../hooks/useLoading"; @@ -10,7 +11,7 @@ import useUserStore from "../stores/v1/user"; const SignIn: React.FC = () => { const { t } = useTranslation(); - const navigate = useNavigate(); + const navigateTo = useNavigateTo(); const userStore = useUserStore(); const workspaceStore = useWorkspaceStore(); const [email, setEmail] = useState(""); @@ -20,7 +21,7 @@ const SignIn: React.FC = () => { useEffect(() => { if (userStore.getCurrentUser()) { - return navigate("/", { + return navigateTo("/", { replace: true, }); } @@ -52,7 +53,7 @@ const SignIn: React.FC = () => { await api.signin(email, password); const user = await userStore.fetchCurrentUser(); if (user) { - navigate("/", { + navigateTo("/", { replace: true, }); } else { diff --git a/frontend/web/src/pages/SignUp.tsx b/frontend/web/src/pages/SignUp.tsx index fd93d8e..cefedc0 100644 --- a/frontend/web/src/pages/SignUp.tsx +++ b/frontend/web/src/pages/SignUp.tsx @@ -2,7 +2,8 @@ import { Button, Input } from "@mui/joy"; import React, { FormEvent, useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; -import { Link, useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; +import useNavigateTo from "@/hooks/useNavigateTo"; import useWorkspaceStore from "@/stores/v1/workspace"; import * as api from "../helpers/api"; import useLoading from "../hooks/useLoading"; @@ -10,7 +11,7 @@ import useUserStore from "../stores/v1/user"; const SignUp: React.FC = () => { const { t } = useTranslation(); - const navigate = useNavigate(); + const navigateTo = useNavigateTo(); const userStore = useUserStore(); const workspaceStore = useWorkspaceStore(); const [email, setEmail] = useState(""); @@ -21,13 +22,13 @@ const SignUp: React.FC = () => { useEffect(() => { if (userStore.getCurrentUser()) { - return navigate("/", { + return navigateTo("/", { replace: true, }); } if (!workspaceStore.setting.enableSignup) { - return navigate("/auth", { + return navigateTo("/auth", { replace: true, }); } @@ -59,7 +60,7 @@ const SignUp: React.FC = () => { await api.signup(email, nickname, password); const user = await userStore.fetchCurrentUser(); if (user) { - navigate("/", { + navigateTo("/", { replace: true, }); } else {