145 lines
5.9 KiB
TypeScript
145 lines
5.9 KiB
TypeScript
import { SevenDayActivityInfo, SevenDayRewardItem, SevenDayRewardReceipt } from "./SevenDayGiftApi";
|
||
import SevenDayGiftReward from "./SevenDayGiftReward";
|
||
|
||
type ClaimHandler = (callback: (success: boolean) => void) => void;
|
||
|
||
export interface SevenDayGiftRuntimeView {
|
||
playRewardEffect(items: SevenDayRewardItem[], day: number, done: () => void): void;
|
||
setData(info: SevenDayActivityInfo, claimHandler: ClaimHandler): void;
|
||
setCloseHandler(closeHandler: () => void): void;
|
||
// 绑定正式领取回调,再展示活动。
|
||
show(autoClaim?: boolean): void;
|
||
}
|
||
|
||
/** Bundle 内的正式业务逻辑,只处理活动领取、发奖和状态刷新。 */
|
||
export default class SevenDayGiftRuntime {
|
||
private info: SevenDayActivityInfo = null;
|
||
private claiming = false;
|
||
private loadingSequence = 0;
|
||
private loadingTimer: any = null;
|
||
private loadingVisible = false;
|
||
private disposed = false;
|
||
|
||
constructor(private view: SevenDayGiftRuntimeView) { }
|
||
|
||
// 接收主页传入的快照和关闭回调。
|
||
public initialize(info: SevenDayActivityInfo, closeHandler: () => void) {
|
||
if (this.disposed) return;
|
||
this.view.setCloseHandler(closeHandler);
|
||
if (info) this.updateInfo(info);
|
||
}
|
||
|
||
// 领取期间不覆盖正在使用的活动快照。
|
||
public updateInfo(info: SevenDayActivityInfo) {
|
||
if (this.disposed || this.claiming || !info) return;
|
||
this.info = info;
|
||
this.view.setData(this.info, this.claimReward.bind(this));
|
||
}
|
||
|
||
public show(autoClaim: boolean = false) {
|
||
if (!this.info || this.disposed) return;
|
||
this.view.setData(this.info, this.claimReward.bind(this));
|
||
this.view.show(autoClaim);
|
||
}
|
||
|
||
// 销毁后忽略异步结果,并清理加载提示。
|
||
public destroy() {
|
||
if (this.disposed) return;
|
||
this.disposed = true;
|
||
this.closeHomeLoading(this.loadingSequence);
|
||
this.claiming = false;
|
||
this.info = null;
|
||
this.view = null;
|
||
}
|
||
|
||
// 从当前查询快照生成奖励记录,同步成功后更新状态并播放效果。
|
||
private claimReward(callback: (success: boolean) => void) {
|
||
if (this.disposed || this.claiming || !this.info || !this.info.canClaim || this.info.todayClaimed) {
|
||
callback(false);
|
||
return;
|
||
}
|
||
const rewardDay = Number(this.info.nextRewardDay);
|
||
const reward = Array.isArray(this.info.rewards)
|
||
? this.info.rewards.find(item => Number(item.day) === rewardDay) : null;
|
||
if (!this.info.activityId || !this.info.startAt || !reward || reward.claimed || !Array.isArray(reward.items) || reward.items.length === 0) {
|
||
cc.warn("[SevenDayGift] 活动查询没有返回可领取奖励");
|
||
callback(false);
|
||
return;
|
||
}
|
||
// 消费主页查询的快照,不再次请求活动接口。UID 由 Reward 加入本地防重键。
|
||
const claimedCount = Math.max(Number(this.info.claimedCount) || 0, rewardDay);
|
||
const receipt: SevenDayRewardReceipt = {
|
||
claimId: JSON.stringify([this.info.activityId, this.info.startAt, rewardDay]),
|
||
rewardDay,
|
||
rewards: reward.items,
|
||
claimedCount,
|
||
todayClaimed: true,
|
||
completed: claimedCount >= 7,
|
||
};
|
||
this.claiming = true;
|
||
const loadingToken = this.openHomeLoading(7000);
|
||
SevenDayGiftReward.grant(receipt, (success: boolean, message?: string) => {
|
||
if (this.disposed) return;
|
||
this.claiming = false;
|
||
this.closeHomeLoading(loadingToken);
|
||
if (!success) {
|
||
cc.warn("[SevenDayGift] 奖励同步失败", message);
|
||
callback(false);
|
||
return;
|
||
}
|
||
this.applyClaimState(receipt.rewardDay, receipt.claimedCount, receipt.completed);
|
||
this.view.playRewardEffect(receipt.rewards, receipt.rewardDay, () => {
|
||
if (!this.disposed) callback(true);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 本地标记当天已领,第七天领取后标记活动完成。
|
||
private applyClaimState(rewardDay: number, claimedCount: number, completed: boolean) {
|
||
if (!this.info) return;
|
||
this.info.claimedCount = Number(claimedCount) || this.info.claimedCount;
|
||
this.info.todayClaimed = true;
|
||
this.info.canClaim = false;
|
||
if (completed) this.info.status = "completed";
|
||
if (Array.isArray(this.info.rewards)) {
|
||
this.info.rewards.forEach(reward => {
|
||
if (Number(reward.day) === Number(rewardDay)) reward.claimed = true;
|
||
});
|
||
}
|
||
this.view.setData(this.info, this.claimReward.bind(this));
|
||
}
|
||
|
||
// 打开主页加载提示,设置超时以免一直遮挡。
|
||
private openHomeLoading(timeoutMs: number): number {
|
||
const token = ++this.loadingSequence;
|
||
if (this.loadingTimer !== null) clearTimeout(this.loadingTimer);
|
||
const home = this.getHomeController();
|
||
if (home && typeof home.openLoad === "function") {
|
||
this.loadingVisible = true;
|
||
home.openLoad();
|
||
}
|
||
this.loadingTimer = setTimeout(() => this.closeHomeLoading(token), timeoutMs);
|
||
return token;
|
||
}
|
||
|
||
// 仅关闭对应请求的提示,避免旧计时器干扰新请求。
|
||
private closeHomeLoading(token: number) {
|
||
if (token !== this.loadingSequence) return;
|
||
if (this.loadingTimer !== null) {
|
||
clearTimeout(this.loadingTimer);
|
||
this.loadingTimer = null;
|
||
}
|
||
if (!this.loadingVisible) return;
|
||
this.loadingVisible = false;
|
||
const home = this.getHomeController();
|
||
if (home && typeof home.closeLoad === "function") home.closeLoad();
|
||
}
|
||
|
||
// 通过主页组件调用现有加载提示。
|
||
private getHomeController(): any {
|
||
const canvas = cc.find("Canvas");
|
||
return canvas && canvas.getComponent("JiaZai");
|
||
}
|
||
|
||
}
|