forked from sk1982/actaeon
fix: keep same page in router.back in hash navigation
This commit is contained in:
parent
b0590c81f2
commit
6d0f663c7d
@ -19,6 +19,7 @@ import { FriendRequest, acceptFriendRequest, getFriendRequests, rejectFriendRequ
|
|||||||
import { useWindowListener } from '@/helpers/use-window-listener';
|
import { useWindowListener } from '@/helpers/use-window-listener';
|
||||||
import { useErrorModal } from '@/components/error-modal';
|
import { useErrorModal } from '@/components/error-modal';
|
||||||
import { useSwipeable } from 'react-swipeable';
|
import { useSwipeable } from 'react-swipeable';
|
||||||
|
import { useReloaded } from '@/components/client-providers';
|
||||||
|
|
||||||
export type HeaderSidebarProps = {
|
export type HeaderSidebarProps = {
|
||||||
children?: React.ReactNode,
|
children?: React.ReactNode,
|
||||||
@ -44,6 +45,8 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
const [userDropdownOpen, setUserDropdownOpen] = useState(false);
|
const [userDropdownOpen, setUserDropdownOpen] = useState(false);
|
||||||
const [menuTranslate, setMenuTranslate] = useState<number | null>(null);
|
const [menuTranslate, setMenuTranslate] = useState<number | null>(null);
|
||||||
const [notificationsTranslate, setNotificationsTranslate] = useState<number | null>(null);
|
const [notificationsTranslate, setNotificationsTranslate] = useState<number | null>(null);
|
||||||
|
const reloaded = useReloaded();
|
||||||
|
const menusOpened = useRef(false);
|
||||||
|
|
||||||
const path = pathname === '/' ? (user?.homepage ?? '/dashboard') : pathname;
|
const path = pathname === '/' ? (user?.homepage ?? '/dashboard') : pathname;
|
||||||
|
|
||||||
@ -54,6 +57,13 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
|
|
||||||
const setError = useErrorModal();
|
const setError = useErrorModal();
|
||||||
|
|
||||||
|
const back = useCallback(() => {
|
||||||
|
if (document.referrer.includes(location.origin) || reloaded || menusOpened.current)
|
||||||
|
router.back();
|
||||||
|
else
|
||||||
|
router.replace('', { scroll: false });
|
||||||
|
}, [router, reloaded]);
|
||||||
|
|
||||||
const { ref } = useSwipeable({
|
const { ref } = useSwipeable({
|
||||||
touchEventOptions: { passive: false },
|
touchEventOptions: { passive: false },
|
||||||
onSwiped: e => {
|
onSwiped: e => {
|
||||||
@ -63,11 +73,11 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
|
|
||||||
if (menuTranslate && ((Math.abs(menuTranslate) > 384 * 0.5) || fastSwipe)) {
|
if (menuTranslate && ((Math.abs(menuTranslate) > 384 * 0.5) || fastSwipe)) {
|
||||||
if (isMenuOpen && window.location.hash === '#sidebar')
|
if (isMenuOpen && window.location.hash === '#sidebar')
|
||||||
router.back();
|
back();
|
||||||
setMenuOpen(!isMenuOpen);
|
setMenuOpen(!isMenuOpen);
|
||||||
} else if (notificationsTranslate && ((Math.abs(notificationsTranslate) > window.innerWidth * 0.4 || fastSwipe))) {
|
} else if (notificationsTranslate && ((Math.abs(notificationsTranslate) > window.innerWidth * 0.4 || fastSwipe))) {
|
||||||
if (isNotificationsOpen && window.location.hash === '#notifications')
|
if (isNotificationsOpen && window.location.hash === '#notifications')
|
||||||
router.back();
|
back();
|
||||||
setNotificationsOpen(!isNotificationsOpen);
|
setNotificationsOpen(!isNotificationsOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,11 +97,6 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
return;
|
return;
|
||||||
parent = parent.parentElement;
|
parent = parent.parentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = (e.event.target as HTMLElement)?.dataset;
|
|
||||||
if (data?.slot === 'thumb' || data?.dragging)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const allMenusClosed = !isMenuOpen && !isNotificationsOpen;
|
const allMenusClosed = !isMenuOpen && !isNotificationsOpen;
|
||||||
const xPercent = e.initial[0] / window.innerWidth;
|
const xPercent = e.initial[0] / window.innerWidth;
|
||||||
|
|
||||||
@ -126,16 +131,22 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
getFriendRequests().then(setFriendRequests);
|
getFriendRequests().then(setFriendRequests);
|
||||||
}, [pathname, user]);
|
}, [pathname, user]);
|
||||||
|
|
||||||
const setNotificationsOpen = (open: boolean) => {
|
const setNotificationsOpen = (open: boolean, updateRef = true) => {
|
||||||
_setNotificationsOpen(open);
|
_setNotificationsOpen(open);
|
||||||
if (open)
|
if (open) {
|
||||||
router.push('#notifications', { scroll: false });
|
router.push('#notifications', { scroll: false });
|
||||||
|
if (updateRef)
|
||||||
|
menusOpened.current = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setMenuOpen = (open: boolean) => {
|
const setMenuOpen = (open: boolean, updateRef = true) => {
|
||||||
_setMenuOpen(open);
|
_setMenuOpen(open);
|
||||||
if (open)
|
if (open) {
|
||||||
router.push('#sidebar', { scroll: false });
|
router.push('#sidebar', { scroll: false });
|
||||||
|
if (updateRef)
|
||||||
|
menusOpened.current = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useWindowListener('hashchange', () => {
|
useWindowListener('hashchange', () => {
|
||||||
@ -144,8 +155,8 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMenuOpen(window.location.hash === '#sidebar');
|
setMenuOpen(window.location.hash === '#sidebar', false);
|
||||||
setNotificationsOpen(window.location.hash === '#notifications');
|
setNotificationsOpen(window.location.hash === '#notifications', false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const renderHeaderLink = useCallback((route: Subroute) => {
|
const renderHeaderLink = useCallback((route: Subroute) => {
|
||||||
@ -310,7 +321,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
<div className={`transition bg-black z-[49] absolute inset-0 w-full h-full ${isMenuOpen ? 'bg-opacity-25' : 'bg-opacity-0 pointer-events-none'}`} onClick={() => {
|
<div className={`transition bg-black z-[49] absolute inset-0 w-full h-full ${isMenuOpen ? 'bg-opacity-25' : 'bg-opacity-0 pointer-events-none'}`} onClick={() => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
if (window.location.hash === '#sidebar')
|
if (window.location.hash === '#sidebar')
|
||||||
router.back();
|
back();
|
||||||
}} />
|
}} />
|
||||||
|
|
||||||
<div className={`dark flex flex-col text-white absolute p-6 top-0 h-full max-w-full w-96 bg-gray-950 z-[49] left-0 ${menuTranslate ? '' : 'transition-all'} ${isMenuOpen ? 'shadow-2xl' : '-translate-x-full'}`} style={menuTranslate ? {
|
<div className={`dark flex flex-col text-white absolute p-6 top-0 h-full max-w-full w-96 bg-gray-950 z-[49] left-0 ${menuTranslate ? '' : 'transition-all'} ${isMenuOpen ? 'shadow-2xl' : '-translate-x-full'}`} style={menuTranslate ? {
|
||||||
@ -321,7 +332,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
if (window.location.hash === '#sidebar')
|
if (window.location.hash === '#sidebar')
|
||||||
router.back();
|
back();
|
||||||
}}>
|
}}>
|
||||||
<ChevronLeftIcon className="h-6 mt-0.5" />
|
<ChevronLeftIcon className="h-6 mt-0.5" />
|
||||||
<span>{routeGroup.title}</span>
|
<span>{routeGroup.title}</span>
|
||||||
@ -329,7 +340,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
<Button className="ml-auto" isIconOnly color="danger" onClick={() => {
|
<Button className="ml-auto" isIconOnly color="danger" onClick={() => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
if (window.location.hash === '#sidebar')
|
if (window.location.hash === '#sidebar')
|
||||||
router.back();
|
back();
|
||||||
}}>
|
}}>
|
||||||
<XMarkIcon className="w-5" />
|
<XMarkIcon className="w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
@ -384,7 +395,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
}, [isMenuOpen, router, menuTranslate, routeGroup, user, path, cookies]);
|
}, [isMenuOpen, router, menuTranslate, routeGroup, user, path, cookies, back]);
|
||||||
|
|
||||||
const rightSidebar = useMemo(() => {
|
const rightSidebar = useMemo(() => {
|
||||||
return (<div className={`fixed inset-0 w-full h-full max-h-full z-[49] ${isNotificationsOpen ? '' : 'pointer-events-none'}`}>
|
return (<div className={`fixed inset-0 w-full h-full max-h-full z-[49] ${isNotificationsOpen ? '' : 'pointer-events-none'}`}>
|
||||||
@ -407,7 +418,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
<Button isIconOnly className="ml-2" color="danger" onPress={() => {
|
<Button isIconOnly className="ml-2" color="danger" onPress={() => {
|
||||||
setNotificationsOpen(false);
|
setNotificationsOpen(false);
|
||||||
if (window.location.hash === '#notifications')
|
if (window.location.hash === '#notifications')
|
||||||
router.back();
|
back();
|
||||||
}}>
|
}}>
|
||||||
<XMarkIcon className="h-1/2" />
|
<XMarkIcon className="h-1/2" />
|
||||||
</Button>
|
</Button>
|
||||||
@ -444,7 +455,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
}, [isNotificationsOpen, notificationsTranslate, user, friendRequests, notifications, router]);
|
}, [isNotificationsOpen, notificationsTranslate, user, friendRequests, notifications, router, back]);
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
{ leftSidebar }
|
{ leftSidebar }
|
||||||
|
@ -1,22 +1,50 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode, createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import { NextUIProvider } from '@nextui-org/react';
|
import { NextUIProvider } from '@nextui-org/react';
|
||||||
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
||||||
import { ErrorProvider } from '@/components/error-modal';
|
import { ErrorProvider } from '@/components/error-modal';
|
||||||
import { ConfirmProvider } from '@/components/confirm-modal';
|
import { ConfirmProvider } from '@/components/confirm-modal';
|
||||||
import { PromptProvider } from '@/components/prompt-modal';
|
import { PromptProvider } from '@/components/prompt-modal';
|
||||||
|
import { useWindowListener } from '@/helpers/use-window-listener';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
export function ClientProviders({ children }: { children: ReactNode }) {
|
const ReloadContext = createContext(false);
|
||||||
return (<ErrorProvider>
|
|
||||||
<ConfirmProvider>
|
export const useReloaded = () => useContext(ReloadContext);
|
||||||
<PromptProvider>
|
|
||||||
<NextUIProvider className="h-full flex">
|
export function ClientProviders({ children }: { children: ReactNode; }) {
|
||||||
<NextThemesProvider attribute="class" defaultTheme="dark" enableSystem>
|
const [isReloaded, setReloaded] = useState(false);
|
||||||
{children}
|
const pathname = usePathname();
|
||||||
</NextThemesProvider>
|
const lastPathname = useRef<string | null>(null);
|
||||||
</NextUIProvider>
|
|
||||||
</PromptProvider>
|
useEffect(() => {
|
||||||
</ConfirmProvider>
|
const reloaded = sessionStorage.getItem('reload');
|
||||||
</ErrorProvider>);
|
if (reloaded && Date.now() - +reloaded < 1000)
|
||||||
|
setReloaded(true);
|
||||||
|
sessionStorage.removeItem('reload');
|
||||||
|
}, []);
|
||||||
|
useWindowListener('beforeunload', () => sessionStorage.setItem('reload', Date.now().toString()));
|
||||||
|
useEffect(() => {
|
||||||
|
if (lastPathname.current !== null) {
|
||||||
|
if (lastPathname.current !== pathname)
|
||||||
|
setReloaded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPathname.current = pathname;
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
|
return (<ReloadContext.Provider value={isReloaded}>
|
||||||
|
<ErrorProvider>
|
||||||
|
<ConfirmProvider>
|
||||||
|
<PromptProvider>
|
||||||
|
<NextUIProvider className="h-full flex">
|
||||||
|
<NextThemesProvider attribute="class" defaultTheme="dark" enableSystem>
|
||||||
|
{children}
|
||||||
|
</NextThemesProvider>
|
||||||
|
</NextUIProvider>
|
||||||
|
</PromptProvider>
|
||||||
|
</ConfirmProvider>
|
||||||
|
</ErrorProvider>
|
||||||
|
</ReloadContext.Provider>);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import { useDebounceCallback } from 'usehooks-ts';
|
|||||||
import { CellMeasurerChildProps } from 'react-virtualized/dist/es/CellMeasurer';
|
import { CellMeasurerChildProps } from 'react-virtualized/dist/es/CellMeasurer';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useWindowListener } from '@/helpers/use-window-listener';
|
import { useWindowListener } from '@/helpers/use-window-listener';
|
||||||
|
import { useReloaded } from './client-providers';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -205,6 +206,8 @@ export const SelectModalButton = <T extends 'grid' | 'list', D extends { name?:
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isOpen, setOpen] = useState(false);
|
const [isOpen, setOpen] = useState(false);
|
||||||
const { modalId, footer, onSelectionChanged, gap, onSelected, selectedItem, renderItem, items, colSize, rowSize, displayMode, modalSize } = props;
|
const { modalId, footer, onSelectionChanged, gap, onSelected, selectedItem, renderItem, items, colSize, rowSize, displayMode, modalSize } = props;
|
||||||
|
const historyPushed = useRef(false);
|
||||||
|
const reloaded = useReloaded();
|
||||||
|
|
||||||
useWindowListener('hashchange', () => {
|
useWindowListener('hashchange', () => {
|
||||||
if (window.location.hash !== `#modal-${modalId}` && isOpen) {
|
if (window.location.hash !== `#modal-${modalId}` && isOpen) {
|
||||||
@ -224,11 +227,16 @@ export const SelectModalButton = <T extends 'grid' | 'list', D extends { name?:
|
|||||||
onSelected={item => {
|
onSelected={item => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
onSelected(item);
|
onSelected(item);
|
||||||
router.back();
|
if (document.referrer.includes(location.origin) || historyPushed.current || reloaded) {
|
||||||
|
router.back();
|
||||||
|
} else {
|
||||||
|
router.replace('', { scroll: false });
|
||||||
|
}
|
||||||
}} />
|
}} />
|
||||||
<Button {...(props as object)} onClick={() => {
|
<Button {...(props as object)} onClick={() => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
router.push(`#modal-${modalId}`,{ scroll: false });
|
router.push(`#modal-${modalId}`, { scroll: false });
|
||||||
|
historyPushed.current = true;
|
||||||
}} />
|
}} />
|
||||||
</>);
|
</>);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user