feat: add Vite entry point and root App component
This commit is contained in:
69
src/App.tsx
Normal file
69
src/App.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { css } from '@emotion/react';
|
||||
import { useEffect } from 'react';
|
||||
import { BottomTabBar } from './components/BottomTabBar';
|
||||
import { OfflineRewardModal } from './components/OfflineRewardModal';
|
||||
import { AchievementToast } from './components/AchievementToast';
|
||||
import { ElementsScreen } from './components/screens/ElementsScreen';
|
||||
import { EvolutionScreen } from './components/screens/EvolutionScreen';
|
||||
import { FusionScreen } from './components/screens/FusionScreen';
|
||||
import { ShopScreen } from './components/screens/ShopScreen';
|
||||
import { AchievementsScreen } from './components/screens/AchievementsScreen';
|
||||
import { SettingsScreen } from './components/screens/SettingsScreen';
|
||||
import { TutorialOverlay } from './components/tutorial/TutorialOverlay';
|
||||
import { useIdleTick } from './hooks/useIdleTick';
|
||||
import { useGameStore } from './store/useGameStore';
|
||||
import { trackGameEvent } from './platform/analytics';
|
||||
|
||||
const rootStyle = css`
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f7f8fa;
|
||||
overflow: hidden;
|
||||
font-family:
|
||||
'Pretendard',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
sans-serif;
|
||||
`;
|
||||
|
||||
const contentStyle = css`
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 72px;
|
||||
`;
|
||||
|
||||
export function App() {
|
||||
const { activeTab, setActiveTab, elements, gold, elementLevels } = useGameStore();
|
||||
useIdleTick();
|
||||
|
||||
useEffect(() => {
|
||||
const ownedCount = Object.values(elements).filter((c) => c > 0).length;
|
||||
const totalLevel = Object.values(elementLevels).reduce((sum, lv) => sum + lv, 0);
|
||||
trackGameEvent('app_open', {
|
||||
platform: 'web',
|
||||
platform_time: new Date().toISOString(),
|
||||
owned_element_count: ownedCount,
|
||||
gold,
|
||||
total_enhance_level: totalLevel,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div css={rootStyle}>
|
||||
<OfflineRewardModal />
|
||||
<AchievementToast />
|
||||
<div css={contentStyle}>
|
||||
{activeTab === 'elements' && <ElementsScreen />}
|
||||
{activeTab === 'evolution' && <EvolutionScreen />}
|
||||
{activeTab === 'fusion' && <FusionScreen />}
|
||||
{activeTab === 'shop' && <ShopScreen />}
|
||||
{activeTab === 'achievements' && <AchievementsScreen />}
|
||||
{activeTab === 'settings' && <SettingsScreen />}
|
||||
</div>
|
||||
<BottomTabBar activeTab={activeTab} onTabChange={setActiveTab} />
|
||||
<TutorialOverlay />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
src/main.tsx
Normal file
23
src/main.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { App } from './App';
|
||||
import { initAnalytics } from './platform/analytics';
|
||||
import { useGameStore, flushGameState } from './store/useGameStore';
|
||||
|
||||
initAnalytics();
|
||||
|
||||
// 탭 종료/이탈 시 즉시 저장 (throttledStorage 보호)
|
||||
window.addEventListener('beforeunload', flushGameState);
|
||||
window.addEventListener('pagehide', flushGameState);
|
||||
|
||||
// 첫 진입 시 오프라인 보상 계산
|
||||
useGameStore.getState().initOffline();
|
||||
|
||||
const rootEl = document.getElementById('root');
|
||||
if (!rootEl) throw new Error('Root element #root not found in index.html');
|
||||
|
||||
createRoot(rootEl).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
Reference in New Issue
Block a user