From ca553aface74f73e126d571e73dd85f9323d2b9b Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 27 Apr 2026 08:36:18 +0900 Subject: [PATCH] feat: add Vite entry point and root App component --- src/App.tsx | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.tsx | 23 ++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/App.tsx create mode 100644 src/main.tsx diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..bee594d --- /dev/null +++ b/src/App.tsx @@ -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 ( +
+ + +
+ {activeTab === 'elements' && } + {activeTab === 'evolution' && } + {activeTab === 'fusion' && } + {activeTab === 'shop' && } + {activeTab === 'achievements' && } + {activeTab === 'settings' && } +
+ + +
+ ); +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..2eeddd6 --- /dev/null +++ b/src/main.tsx @@ -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( + + + +);