import { WxSDK } from './WxSDK'; import { ShareManager } from './ShareManager'; import { AuthManager } from './AuthManager'; import { ViewManager } from '../core/ViewManager'; /** * 分享启动监听器 * * 微信小游戏未被杀掉、只是退到后台时,再次通过好友分享卡片打开小游戏, * 不会重新走启动链路(PageLoading 不会再跑),因此 `wx.getLaunchOptionsSync()` * 取到的 query 可能是上一次启动的旧值。这个 handler 通过 `wx.onShow` * 拿到最新的 query,检测 shareCode 变化后: * 1. 清掉旧的分享态 * 2. 调用 `ShareManager.joinShare(code)` 拉取新的题单 * 3. 直接打开 `PageLevel` 进入分享挑战 * * 对应在 PageLevel 那侧通过 `onViewShow` 检测到 ShareManager.shareCode * 变化,重新走 `_reinitLevelSession`。 */ export class ShareLaunchHandler { private static _instance: ShareLaunchHandler | null = null; static get instance(): ShareLaunchHandler { if (!this._instance) { this._instance = new ShareLaunchHandler(); } return this._instance; } /** 已经处理过的 shareCode,相同则不再重复 join */ private _activeShareCode: string | null = null; /** 是否正在处理一次 onShow 触发的 join 流程,避免并发 */ private _isHandlingShow: boolean = false; /** 是否已经初始化 */ private _initialized: boolean = false; private _showHandler = (res: { query?: Record } | undefined) => { const code = WxSDK.extractShareCodeFromQuery(res?.query); if (!code) { return; } // 同一个 shareCode 且当前已经处于该分享态,无需重复处理 if (code === this._activeShareCode && ShareManager.instance.isShareMode) { return; } // 即使不是新的 code,但如果 ShareManager 已经丢失了分享态(例如挑战完成被 clear), // 用户重新点同一个分享卡片仍然应当重新加入。 void this._handleShareCode(code); }; private _hideHandler = () => { // 目前不在 onHide 时做任何破坏性操作;保留监听只为方便后续扩展 // (比如:暂停倒计时、上报埋点)。 console.log('[ShareLaunchHandler] 小游戏切到后台'); }; /** * 在 main.onLoad 中调用,注册 wx.onShow / wx.onHide。 * 同时把当前启动参数中的 shareCode 作为种子,避免初次冷启动时 * 因 wx.onShow 也会被调用一次而重复触发分享流程。 */ init(): void { if (this._initialized) { return; } this._initialized = true; if (!WxSDK.isWechat()) { return; } // 冷启动时先把当前 launch 中的 shareCode 标记成已处理, // 避免 wx.onShow 在初始展示时拿到同一个 code 又走一遍 join。 this._activeShareCode = WxSDK.getShareCodeFromLaunch(); WxSDK.onAppShow(this._showHandler); WxSDK.onAppHide(this._hideHandler); console.log('[ShareLaunchHandler] 已注册 onShow/onHide 监听'); } /** * 由 PageLoading 在初始 join 之后调用,把已处理的 shareCode 显式同步过来, * 让 onShow 收到相同 code 时不会重复 join。 */ markActiveShareCode(code: string | null): void { this._activeShareCode = code; } /** * 主动取消监听(一般无需调用,留作扩展) */ dispose(): void { if (!this._initialized) return; this._initialized = false; WxSDK.offAppShow(this._showHandler); WxSDK.offAppHide(this._hideHandler); } private async _handleShareCode(code: string): Promise { if (this._isHandlingShow) { console.log('[ShareLaunchHandler] 已有 onShow 分享流程在执行,跳过', code); return; } this._isHandlingShow = true; try { console.log('[ShareLaunchHandler] 检测到新的 shareCode,准备切换:', code); // 确保已登录(initialize 内部对已有 token 做了校验,幂等可重复调用) const loginOk = await AuthManager.instance.initialize(); if (!loginOk) { console.warn('[ShareLaunchHandler] 登录失败,放弃 onShow 分享切换'); return; } // 切到新的分享前清掉旧分享态,避免 ShareManager 内残留旧题单导致 PageLevel 错位 if (ShareManager.instance.isShareMode) { ShareManager.instance.clearShareMode(); } const joinOk = await ShareManager.instance.joinShare(code); if (!joinOk) { console.warn('[ShareLaunchHandler] 加入分享失败:', code); return; } // 标记当前激活的分享码 this._activeShareCode = code; // 跳过中间页,直接打开 PageLevel 进入分享挑战。 // PageLevel 会在 onViewShow 中根据 ShareManager.shareCode 与本地缓存比对, // 决定是否需要 `_reinitLevelSession`。 ViewManager.instance.open('PageLevel', { params: { shareMode: true }, }); console.log('[ShareLaunchHandler] 已切换到分享挑战:', code); } catch (err) { console.error('[ShareLaunchHandler] 处理 shareCode 异常:', err); } finally { this._isHandlingShow = false; } } }