210 lines
8.0 KiB
Dart
210 lines
8.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:hgdj/hj_model/splash/ads_model.dart';
|
|
import 'package:hgdj/hj_page/main_page/provider/msg_provider.dart';
|
|
import 'package:hgdj/hj_page/pre_sale/pre_sale_provider.dart';
|
|
import 'package:hgdj/alert/vip_guide/guide_manager.dart';
|
|
import 'package:hgdj/alert/vip_guide/timed_popup_manager.dart';
|
|
import 'package:hgdj/tools_base/ad_manager.dart';
|
|
|
|
import '../../alert/splash/app_center_dialog.dart';
|
|
import '../../alert/splash/notice_image_dialog.dart';
|
|
import '../../alert/splash/notice_text_dialog.dart';
|
|
import '../../alert/vip_guide/guide_bottom_sheet.dart';
|
|
import '../../alert/vip_guide/guide_home_dialog.dart';
|
|
import '../../config/config.dart';
|
|
import '../../hj_model/home/plate_model.dart';
|
|
import '../../tools_base/debug_log.dart';
|
|
import '../../tools_base/module_sort_manager.dart';
|
|
import '../mine/mine_vip/mine_charge_vip_page.dart';
|
|
import '../mine/mine_vip/pay_order_source.dart';
|
|
import '../mine/widgets/gradient_text.dart';
|
|
import '../pre_sale/limit_time_provider.dart';
|
|
|
|
class HomeMainLogic extends GetxController {
|
|
final bool isDarkStyle;
|
|
final String? darkDefaultId; // 暗网跳转到指定的tab使用
|
|
HomeMainLogic({
|
|
this.isDarkStyle = false,
|
|
this.darkDefaultId,
|
|
});
|
|
|
|
//开屏弹窗链每次启动只跑一次
|
|
static bool _adShown = false;
|
|
|
|
bool isLoading = true;
|
|
int tabViewKey = 0; //排序变了就 +1 换掉 HomeTabView 的 key,强制重建 TabController
|
|
int _initIndex = 0; // 注意⚠️:暗网进入,有可能通过内链指定亚模块进入,index通过defaultId 处理
|
|
|
|
//合并本地排序后的 tab 列表,_loadTabs 里算一次
|
|
List<ModuleData> tabs = [];
|
|
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
|
//tabCtr 由 HomeTabView 在自身 vsync 下创建并 dispose,这里只持引用驱动 tab 切换;排序时置 null 触发重建
|
|
TabController? tabCtr;
|
|
|
|
String get _sortKey => "_localSortKey_Tab_ID_cache_$isDarkStyle";
|
|
|
|
int get selectedTabIndex => tabCtr?.index ?? _initIndex;
|
|
|
|
//服务端下发的原始顺序(抽屉「恢复默认」用)
|
|
List<ModuleData> get defaultList {
|
|
if (isDarkStyle) return Config.plateModule?.deepWeb ?? [];
|
|
// 固定最新模块
|
|
return [
|
|
ModuleData(id: "-110", moduleName: "最新", type: 1, showType: 1),
|
|
...?Config.plateModule?.homePage,
|
|
];
|
|
}
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_loadTabs();
|
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
|
limitTimeProvider.refresh(); //获取限时活动(放首帧后,避免 build 中 notifyListeners)
|
|
_showAdChain();
|
|
});
|
|
}
|
|
|
|
void openEndDrawer() => scaffoldKey.currentState?.openEndDrawer();
|
|
|
|
void gotoTabId(String tagDataId) {
|
|
final index = tabs.indexWhere((e) => e.id == tagDataId);
|
|
if (index >= 0) tabCtr?.index = index;
|
|
}
|
|
|
|
/// 应用抽屉里的新排序:顺序没变只切 tab,变了就落盘并换 key 重建 TabView
|
|
Future<void> resortTabs(List<ModuleData> dataArr, ModuleData? tagData) async {
|
|
final curTagData = tagData ?? tabs[selectedTabIndex];
|
|
bool isSameArr = true;
|
|
int selectIndex = 0;
|
|
//抽屉只重排不增删,长度必然一致;逐项比名字,顺带定位选中项的新位置
|
|
if (dataArr.length == tabs.length) {
|
|
for (int i = 0; i < dataArr.length; i++) {
|
|
if (dataArr[i].moduleName != tabs[i].moduleName) isSameArr = false;
|
|
if (dataArr[i].moduleName == curTagData.moduleName) selectIndex = i;
|
|
}
|
|
}
|
|
if (isSameArr) {
|
|
tabCtr?.index = selectIndex;
|
|
return;
|
|
}
|
|
await ModuleSortManager().saveLocalData(dataArr, _sortKey);
|
|
tabViewKey++;
|
|
tabs = dataArr;
|
|
_initIndex = selectIndex;
|
|
tabCtr = null; //旧 State 即将销毁,先断引用,由新 State 重新赋值
|
|
update();
|
|
}
|
|
|
|
//合并本地排序与服务端 tab,顺带算好初始选中的 index
|
|
Future<void> _loadTabs() async {
|
|
try {
|
|
tabs = await ModuleSortManager().mergeLocalSortWithServer(
|
|
serverTabs: defaultList,
|
|
key: _sortKey,
|
|
);
|
|
if (darkDefaultId?.isNotEmpty == true) {
|
|
// 初始化指定模块(一般暗网模块用)
|
|
final index = tabs.indexWhere((e) => e.id == darkDefaultId);
|
|
if (index >= 0) _initIndex = index;
|
|
} else if (!isDarkStyle) {
|
|
//默认不显示最新模块,选中「热门」(index 1);但 tab 不足 2 个时回退到 0,避免越界
|
|
_initIndex = (tabs.first.moduleName == "最新" && tabs.length > 1) ? 1 : 0;
|
|
}
|
|
} catch (e) {
|
|
debugLog(e);
|
|
}
|
|
isLoading = false;
|
|
update();
|
|
}
|
|
|
|
/// 首页开屏弹窗链:系统提示 → 图片公告 → 文字公告 → 预售 → 支付分层 → VIP 引导。
|
|
/// 逐个 await 串行弹,每次启动只跑一次
|
|
Future<void> _showAdChain() async {
|
|
if (_adShown) return;
|
|
_adShown = true;
|
|
if (AdManager().showAbTestAd) {
|
|
await _showAppDialog(); //系统提示
|
|
await _showImageDialog(); //图片公告
|
|
await _showWordDialog(); //文字公告
|
|
}
|
|
|
|
await PreSaleProvider().showAlert(); //预售弹窗
|
|
await _showPayDialog(); //支付分层首页弹窗
|
|
await _showVipGuide(); //首页吸底 VIP 引导(全链优先级最后)
|
|
}
|
|
|
|
/// 支付分层首页弹窗:有 homePage 图就弹,倒计时由 lastDiscountTime 自行判断。
|
|
/// 点海报跳会员中心,这里 await 到用户从会员页返回,否则链条后面的吸底引导会弹在会员页上
|
|
Future<void> _showPayDialog() async {
|
|
final config = MineMsgProvider().payTier?.config;
|
|
if (config == null || (config.homePage ?? '').isEmpty) return;
|
|
final goVip = await Get.dialog<bool>(GuideHomeDialog(config: config));
|
|
if (goVip != true) return;
|
|
await Get.to(() => MineChargeVipPage(
|
|
vipID: config.vipCard, sourcePage: PaySourcePage.homeUserSegment));
|
|
}
|
|
|
|
/// 首页吸底 VIP 引导:开屏弹窗链末尾弹(全链最低优先级),能不能弹由 HOME_NEW_USER / HOME_OLD_USER
|
|
/// 两个场景开关决定(后端按分层下发,最多命中一个);文案优先用下发的,没配走默认。
|
|
/// 走 TimedPopupManager 与其它引导共用互斥;每次启动只弹一次(_showAdChain 由静态 _adShown 保证)。
|
|
Future<void> _showVipGuide() async {
|
|
final scene = GuideManager().homeScene;
|
|
if (scene == null) return;
|
|
final config = GuideManager().configOf(scene);
|
|
await TimedPopupManager().trigger(
|
|
canShow: () => GuideManager().canShow(scene),
|
|
onShow: () => GuideBottomSheet.show(
|
|
scene: scene,
|
|
title: Text(
|
|
config?.title?.isNotEmpty == true ? config!.title! : 'VIP影片免费试看',
|
|
style: const TextStyle(
|
|
color: Colors.white, fontSize: 19, fontWeight: FontWeight.w600),
|
|
),
|
|
subtitle: GradientText(
|
|
config?.description?.isNotEmpty == true
|
|
? config!.description!
|
|
: '开通会员 查看完整影片',
|
|
gradient:
|
|
const LinearGradient(colors: [Colors.white, Color(0xffEFD394)]),
|
|
style: const TextStyle(
|
|
color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
///显示公告信息(9宫格应用广告)
|
|
Future<void> _showAppDialog() async {
|
|
final appList = AdManager().adsByType(3);
|
|
if (appList.isEmpty) return;
|
|
await Get.dialog(AppCenterDialog(appList: appList));
|
|
}
|
|
|
|
///显示图片公告(上下双排,每次弹两张)
|
|
Future<void> _showImageDialog() async {
|
|
final adsList = AdManager().adsByType(46);
|
|
for (int i = 0; i < adsList.length; i += 2) {
|
|
await Get.dialog(
|
|
SystemImagesDialog(ads: adsList.skip(i).take(2).toList()));
|
|
}
|
|
}
|
|
|
|
//文字公告
|
|
Future<void> _showWordDialog() async {
|
|
for (final wordBean in AdManager().announceList) {
|
|
await Get.dialog(
|
|
wordBean.type == 0
|
|
? NoticeTextDialog(
|
|
content: wordBean.content.toString(), wordBean: wordBean)
|
|
: NoticeImageDialog(
|
|
model:
|
|
AdsInfoModel(cover: wordBean.cover, href: wordBean.href)),
|
|
);
|
|
}
|
|
}
|
|
}
|