[common] feat: useSessionExtendConfirm 생성

This commit is contained in:
2026-07-08 15:43:42 +09:00
parent 28d54b615b
commit eb84a23d7e
12 changed files with 478 additions and 45 deletions
@@ -4,9 +4,12 @@ import { useEffect, useCallback, useState, useRef } from "react";
import { useQuery } from "@tanstack/react-query";
import { images } from "@common/assets";
import type { AuthService, TokenManager } from "@common/auth";
import { getRemainingMs } from "@common/auth";
import { getConfig } from "@common/config";
import { getAuthLoginRedirectUrl, isAtLoginRedirectUrl } from "@common/network";
import { ENDPOINT } from "../../config";
import { setCookie, getCookie } from "../../lib";
import { useSessionExtendConfirm } from "../../lib/use-session-extend-confirm";
import {
getPopupInfo,
getUserPopupList,
@@ -97,6 +100,35 @@ async function legacyLogout(tokenManager?: TokenManager) {
}
}
function formatRemainingMs(remainingMs: number): string {
if (remainingMs <= 0) return "만료됨";
const totalSeconds = Math.floor(remainingMs / 1000);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
return `${minutes}분 ${seconds}초`;
}
function useTokenRemainingMs(enabled: boolean): number {
const [remainingMs, setRemainingMs] = useState(0);
useEffect(() => {
if (!enabled) return;
const update = () => {
const token = getCookie(appConfig.CLAIM_NAME ?? "");
setRemainingMs(token ? getRemainingMs(token) : 0);
};
update();
const id = setInterval(update, 1000);
return () => clearInterval(id);
}, [enabled]);
return remainingMs;
}
export function Header({
authService,
tokenManager,
@@ -111,6 +143,19 @@ export function Header({
const [logoWidth, setLogoWidth] = useState(0);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const logoRef = useRef<HTMLImageElement>(null);
const remainingMs = useTokenRemainingMs(mounted && isLoggedIn && !viewOnly);
useSessionExtendConfirm({
enabled:
mounted &&
isLoggedIn &&
!viewOnly &&
!!authService &&
!isAtLoginRedirectUrl(getAuthLoginRedirectUrl()),
getAccessToken: () => getCookie(appConfig.CLAIM_NAME ?? ""),
silentRefresh: authService?.silentRefresh ?? (async () => {}),
declineSessionExtend: authService?.declineSessionExtend ?? (() => {}),
});
const { data: userInfo } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS],
@@ -261,18 +306,11 @@ export function Header({
<i className="diveicon di-chevron-down static p-0 transition-transform group-data-[state=open]:rotate-180" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-48 border-border p-1">
<ul>
<li className="cursor-pointer rounded-sm p-3 text-xl hover:bg-accent">
<NoticePopover />
</li>
<li
onClick={handleLogout}
className="cursor-pointer rounded-sm p-3 text-xl hover:bg-accent"
>
로그아웃
</li>
</ul>
<PopoverContent align="end" className="w-48 border-border p-1.5">
<UserMenuContent
remainingMs={remainingMs}
onLogout={handleLogout}
/>
</PopoverContent>
</Popover>
)}
@@ -337,18 +375,11 @@ export function Header({
<i className="diveicon di-chevron-down static p-0 transition-transform group-data-[state=open]:rotate-180" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-48 border-border p-1">
<ul>
<li className="cursor-pointer rounded-sm p-3 text-xl hover:bg-accent">
<NoticePopover />
</li>
<li
onClick={handleLogout}
className="cursor-pointer rounded-sm p-3 text-xl hover:bg-accent"
>
로그아웃
</li>
</ul>
<PopoverContent align="end" className="w-48 border-border p-1.5">
<UserMenuContent
remainingMs={remainingMs}
onLogout={handleLogout}
/>
</PopoverContent>
</Popover>
)
@@ -359,3 +390,31 @@ export function Header({
</header>
);
}
function UserMenuContent({
remainingMs,
onLogout,
}: {
remainingMs: number;
onLogout: () => void;
}) {
return (
<ul>
<li className="rounded-sm px-3 py-2 text-sm text-muted-foreground">
<span className="block text-sm">로그인 만료까지</span>
<span className="block text-lg font-medium text-foreground">
{formatRemainingMs(remainingMs)}
</span>
</li>
<li className="cursor-pointer rounded-sm px-3 py-2 text-xl hover:bg-accent">
<NoticePopover />
</li>
<li
onClick={onLogout}
className="cursor-pointer rounded-sm px-3 py-2 text-xl hover:bg-accent"
>
로그아웃
</li>
</ul>
);
}