feat(app): focus entry surface로 진입 화면 재구성

This commit is contained in:
2026-03-13 09:54:33 +09:00
parent 698c124ade
commit 2506dd53a7
16 changed files with 1346 additions and 30 deletions

View File

@@ -0,0 +1,63 @@
'use client';
import { useCallback, useEffect, useState } from 'react';
import type { PlanTier } from './types';
const PLAN_TIER_STORAGE_KEY = 'viberoom:plan-tier:v1';
const normalizePlanTier = (value: unknown): PlanTier => {
return value === 'pro' ? 'pro' : 'normal';
};
export const readStoredPlanTier = (): PlanTier => {
if (typeof window === 'undefined') {
return 'normal';
}
try {
return normalizePlanTier(window.localStorage.getItem(PLAN_TIER_STORAGE_KEY));
} catch {
return 'normal';
}
};
export const usePlanTier = () => {
const [plan, setPlanState] = useState<PlanTier>('normal');
const [hasHydratedPlan, setHasHydratedPlan] = useState(false);
useEffect(() => {
setPlanState(readStoredPlanTier());
setHasHydratedPlan(true);
const handleStorage = (event: StorageEvent) => {
if (event.key !== PLAN_TIER_STORAGE_KEY) {
return;
}
setPlanState(normalizePlanTier(event.newValue));
};
window.addEventListener('storage', handleStorage);
return () => {
window.removeEventListener('storage', handleStorage);
};
}, []);
const setPlan = useCallback((nextPlan: PlanTier) => {
setPlanState(nextPlan);
if (typeof window === 'undefined') {
return;
}
window.localStorage.setItem(PLAN_TIER_STORAGE_KEY, nextPlan);
}, []);
return {
plan,
hasHydratedPlan,
isPro: plan === 'pro',
setPlan,
};
};