feat(flow): session routing contract 정리
This commit is contained in:
@@ -43,6 +43,7 @@ const entryCopy = {
|
||||
resumeRunning: '진행 중인 세션이 있어요.',
|
||||
resumePaused: '잠시 멈춘 세션이 있어요.',
|
||||
resumeCta: '이어서 들어가기',
|
||||
resumeRouting: '진행 중인 세션으로 돌아가는 중이에요.',
|
||||
resumeMicroStepLabel: '마지막 한 조각',
|
||||
resumeNewGoalHint: '새 목표는 현재 세션을 마무리한 뒤 시작할 수 있어요.',
|
||||
loadFailed: '세션 상태를 불러오지 못했어요. 새로 시작은 계속 할 수 있어요.',
|
||||
@@ -176,6 +177,8 @@ export const FocusDashboardWidget = () => {
|
||||
const reviewTeaserHelper = isPro ? entryCopy.reviewHelperPro : entryCopy.reviewHelper;
|
||||
const reviewTeaserCta = isPro ? entryCopy.reviewCtaPro : entryCopy.reviewCta;
|
||||
const entryRitualHint = reviewEntryPresetConfig ? `추천 ritual · ${reviewEntryPresetConfig.label}` : entryCopy.ritualHint;
|
||||
const isRunningSession = currentSession?.state === 'running';
|
||||
const isPausedSession = currentSession?.state === 'paused';
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -210,6 +213,12 @@ export const FocusDashboardWidget = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCheckingSession && isRunningSession) {
|
||||
router.replace('/space');
|
||||
}
|
||||
}, [isCheckingSession, isRunningSession, router]);
|
||||
|
||||
const openPaywall = () => {
|
||||
if (!isPro) {
|
||||
setPaywallSource('app-entry-plan-pill');
|
||||
@@ -250,13 +259,13 @@ export const FocusDashboardWidget = () => {
|
||||
};
|
||||
|
||||
const handleResumeSession = () => {
|
||||
router.push('/space');
|
||||
router.push('/space?resume=continue');
|
||||
};
|
||||
|
||||
const shouldShowWeeklyReviewTeaser =
|
||||
!isCheckingSession && !currentSession && hasEnoughWeeklyData && !isReviewReturn;
|
||||
const shouldShowResumeReviewEntry =
|
||||
!isCheckingSession && Boolean(currentSession) && hasEnoughWeeklyData;
|
||||
!isCheckingSession && isPausedSession && hasEnoughWeeklyData;
|
||||
|
||||
return (
|
||||
<div className="relative min-h-dvh overflow-hidden bg-slate-950 text-white selection:bg-white/20">
|
||||
@@ -305,7 +314,14 @@ export const FocusDashboardWidget = () => {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{currentSession ? (
|
||||
{isRunningSession ? (
|
||||
<div className={cn(goalCardClass, 'space-y-4 text-center')}>
|
||||
<p className="text-[11px] font-medium uppercase tracking-[0.18em] text-white/46">
|
||||
{entryCopy.resumeEyebrow}
|
||||
</p>
|
||||
<p className="text-[15px] text-white/72">{entryCopy.resumeRouting}</p>
|
||||
</div>
|
||||
) : currentSession ? (
|
||||
<div className={cn(goalCardClass, 'space-y-5')}>
|
||||
<div className="space-y-3">
|
||||
<p className="text-[11px] font-medium uppercase tracking-[0.18em] text-white/46">
|
||||
@@ -315,7 +331,7 @@ export const FocusDashboardWidget = () => {
|
||||
{currentSession.goal}
|
||||
</h1>
|
||||
<p className="text-sm text-white/68">
|
||||
{currentSession.state === 'paused' ? entryCopy.resumePaused : entryCopy.resumeRunning}
|
||||
{entryCopy.resumePaused}
|
||||
</p>
|
||||
{currentSession.microStep ? (
|
||||
<div className="rounded-[1.1rem] border border-white/10 bg-white/[0.04] px-4 py-3">
|
||||
|
||||
@@ -39,6 +39,7 @@ import { FocusTopToast } from "./FocusTopToast";
|
||||
export const SpaceWorkspaceWidget = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const resumeIntent = searchParams.get("resume");
|
||||
const sceneQuery = searchParams.get("scene") ?? searchParams.get("room");
|
||||
const goalQuery = searchParams.get("goal")?.trim() ?? "";
|
||||
const focusPlanItemIdQuery = searchParams.get("planItemId");
|
||||
@@ -222,6 +223,8 @@ export const SpaceWorkspaceWidget = () => {
|
||||
workspaceMode === "setup" &&
|
||||
showReviewTeaserAfterComplete &&
|
||||
hasEnoughWeeklyData;
|
||||
const allowsPausedReentry =
|
||||
resumeIntent === "continue" || resumeIntent === "refocus";
|
||||
const secondaryReviewTeaser = shouldShowSecondaryReviewTeaser
|
||||
? {
|
||||
title: isPro
|
||||
@@ -242,6 +245,16 @@ export const SpaceWorkspaceWidget = () => {
|
||||
}
|
||||
}, [isBootstrapping, currentSession, hasQueryOverrides, router]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isBootstrapping || !currentSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentSession.state === "paused" && !allowsPausedReentry) {
|
||||
router.replace("/app");
|
||||
}
|
||||
}, [allowsPausedReentry, currentSession, isBootstrapping, router]);
|
||||
|
||||
useEffect(() => {
|
||||
const preferMobile =
|
||||
typeof window !== "undefined"
|
||||
|
||||
Reference in New Issue
Block a user