const themeMod = require('./utils/theme') const cloud = require('./utils/cloud') const storage = require('./utils/storage') App({ async onLaunch() { // Init cloud storage (no-op if not configured) cloud.init() const settings = wx.getStorageSync('user_settings') if (!settings) { wx.setStorageSync('user_settings', { planId: 'beginner', voiceGuide: true, vibrate: true, planStartDate: storage.getToday() }) } else if (!settings.planStartDate) { // Migrate: add planStartDate for existing users settings.planStartDate = storage.getToday() wx.setStorageSync('user_settings', settings) } const streak = wx.getStorageSync('current_streak') if (!streak) { wx.setStorageSync('current_streak', { count: 0, lastDate: '' }) } this.globalData.theme = themeMod.getCurrentTheme() // Sync with cloud: always locate our doc first (pullAll caches // _openid / _docId), then either restore or push. The previous flow // only pulled when local was empty, so on a cold start with non-empty // local it pushed without locating — and because the module-level // _openid / _docId caches had been reset, pushAll fell through to // add() and created a duplicate cloud doc every cold start. The // leaderboard then summed duration across N docs for the same user. const records = wx.getStorageSync('training_records') if (cloud.enabled) { try { const cloudData = await cloud.pullAll() if (!records || Object.keys(records).length === 0) { // Case 1: Fresh install (or user cleared local) — restore from cloud this._restoreFromCloud(cloudData) } else { // Case 2: Has local data — push to cloud (local is authoritative) // pullAll already populated _openid / _docId, so pushAll will // update the existing doc instead of creating a duplicate. cloud.pushAll() } } catch (e) { console.error('Cloud sync on launch failed:', e) } } }, _restoreFromCloud(cloudData) { if (!cloudData) return try { if (cloudData.records && Object.keys(cloudData.records).length > 0) { wx.setStorageSync('training_records', cloudData.records) } if (cloudData.settings) { wx.setStorageSync('user_settings', cloudData.settings) } if (cloudData.streak) { wx.setStorageSync('current_streak', cloudData.streak) } if (cloudData.customPlans && typeof cloudData.customPlans === 'object') { wx.setStorageSync('custom_plans', cloudData.customPlans) } if (cloudData.themeId) { themeMod.setTheme(cloudData.themeId) } } catch (e) { console.error('Cloud restore failed:', e) } }, globalData: { planId: 'beginner', theme: null } })