import 'package:get/get.dart'; import 'package:nomo/app/controllers/api_controller.dart'; import 'package:nomo/utils/misc.dart'; import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; import '../../../../utils/system_helper.dart'; import '../../../controllers/base_core_api.dart'; import '../../../../utils/awesome_notifications_helper.dart'; import '../../../../utils/log/logger.dart'; import '../../../base/base_controller.dart'; import '../../../constants/enums.dart'; import '../../../controllers/core_controller.dart'; import '../../../data/models/banner/banner_list.dart'; import '../../../data/models/launch/groups.dart'; import '../../../data/sp/ix_sp.dart'; import '../../../dialog/error_dialog.dart'; import '../../../routes/app_pages.dart'; /// 主页控制器 class HomeController extends BaseController { final coreController = Get.find(); final apiController = Get.find(); final TAG = 'HomeController'; final _refreshController = RefreshController(initialRefresh: false); RefreshController get refreshController => _refreshController; final _currentBannerIndex = 0.obs; int get currentBannerIndex => _currentBannerIndex.value; set currentBannerIndex(int value) => _currentBannerIndex.value = value; // 最近位置是否展开 final _isRecentLocationsExpanded = false.obs; bool get isRecentLocationsExpanded => _isRecentLocationsExpanded.value; set isRecentLocationsExpanded(bool value) => _isRecentLocationsExpanded.value = value; /// 收起最近位置列表 void collapseRecentLocations() { if (_isRecentLocationsExpanded.value) { _isRecentLocationsExpanded.value = false; } } // 统计信息 final _uplinkBytes = 0.obs; final _downlinkBytes = 0.obs; int get uplinkBytes => _uplinkBytes.value; int get downlinkBytes => _downlinkBytes.value; // 延迟信息 final _currentDelay = 0.obs; int get currentDelay => _currentDelay.value; // 当前选择的位置 final _selectedLocation = Rxn(); Locations? get selectedLocation => _selectedLocation.value; set selectedLocation(Locations? value) => _selectedLocation.value = value; // 最近使用的位置列表 final _recentLocations = [].obs; List get recentLocations => _recentLocations.where((loc) => loc.id != selectedLocation?.id).toList(); // Banner 列表 final _bannerList = [].obs; List get bannerList => _bannerList; set bannerList(List value) => _bannerList.assignAll(value); // Banner 列表 final _nineBannerList = [].obs; List get nineBannerList => _nineBannerList; set nineBannerList(List value) => _nineBannerList.assignAll(value); @override void onInit() { super.onInit(); _initializeLocations(); getBanner(position: 'nine'); getBanner(position: 'banner'); // 桌面模式不需要应用内通知 if (!isDesktop) { // 延迟100ms后初始化通知 // Future.delayed(const Duration(milliseconds: 100), () { // AwesomeNotificationsHelper.init(); // AwesomeNotificationsHelper.showPushNoticeDialog(); // }); } checkUpdate(); } Future checkUpdate() async { final isVpnRunning = await BaseCoreApi().isConnected() ?? false; if (!isVpnRunning) { await apiController.checkUpdate(); } } /// 初始化位置数据 void _initializeLocations() { // 从 SharedPreferences 加载保存的节点数据 _loadSavedLocations(); } /// 从 SharedPreferences 加载保存的节点数据 void _loadSavedLocations() { try { // 加载当前选中的节点 final selectedLocationData = IXSP.getSelectedLocation(); if (selectedLocationData != null) { final savedLocation = Locations.fromJson(selectedLocationData); // 检查保存的节点是否存在于当前 groups 中 if (_isLocationExistsInGroups(savedLocation)) { selectedLocation = savedLocation; } else { // 如果节点不存在于 groups 中,选中第一个可用节点 // 如果当前节点是连接中的状态,则断开连接 if (coreController.state != ConnectionState.disconnected) { BaseCoreApi().disconnect(); } log( TAG, 'Saved location not found in groups, selecting first available', ); _selectFirstAvailableLocation(); } } else { // 如果没有保存的节点,选中第一个可用节点 _selectFirstAvailableLocation(); } // 加载最近选择的节点列表 final recentLocationsData = IXSP.getRecentLocations(); if (recentLocationsData.isNotEmpty) { _recentLocations.assignAll( recentLocationsData.map((e) => Locations.fromJson(e)).toList(), ); } } catch (e) { log(TAG, 'Error loading saved locations: $e'); } } /// 检查节点是否存在于当前 groups 中 bool _isLocationExistsInGroups(Locations location) { final launch = IXSP.getLaunch(); final groups = launch?.groups; if (groups == null) return false; // 检查 normal 列表 if (groups.normal?.list != null) { for (var locationList in groups.normal!.list!) { if (locationList.locations != null) { for (var loc in locationList.locations!) { if (loc.id == location.id) { return true; } } } } } // 检查 streaming 列表 if (groups.streaming?.list != null) { for (var locationList in groups.streaming!.list!) { if (locationList.locations != null) { for (var loc in locationList.locations!) { if (loc.id == location.id) { return true; } } } } } return false; } /// 选中第一个可用节点 void _selectFirstAvailableLocation() { try { final launch = IXSP.getLaunch(); final normalList = launch?.groups?.normal?.list; if (normalList != null && normalList.isNotEmpty) { // 遍历找到第一个有可用节点的国家 for (var locationList in normalList) { if (locationList.locations != null && locationList.locations!.isNotEmpty) { // 选中第一个节点 final firstLocation = locationList.locations!.first; selectLocation(firstLocation); break; } } } } catch (e) { log(TAG, 'Error selecting first available location: $e'); } } /// 选择位置 void selectLocation( Locations location, { String locationSelectionType = 'auto', }) { selectedLocation = location; coreController.locationSelectionType = locationSelectionType; // 更新最近使用列表 _updateRecentLocations(location); // 保存到 SharedPreferences _saveLocationsToStorage(); } // 从节点列表中选择节点后需要延迟300ms void handleConnect({bool delay = false}) { if (delay) { // 延迟300ms Future.delayed(const Duration(milliseconds: 300), () { coreController.selectLocationConnect(); }); } else { coreController.selectLocationConnect(); } } /// 更新最近使用的位置列表 void _updateRecentLocations(Locations location) { // 移除已存在的位置 _recentLocations.removeWhere((loc) => loc.id == location.id); // 添加到列表开头 _recentLocations.insert(0, location); // 保持最多4个最近使用的位置(过滤掉当前选中后能显示3个) if (_recentLocations.length > 4) { _recentLocations.removeRange(4, _recentLocations.length); } } /// 保存位置数据到 SharedPreferences void _saveLocationsToStorage() { try { // 保存当前选中的节点 if (selectedLocation != null) { IXSP.saveSelectedLocation(selectedLocation!.toJson()); } // 保存最近选择的节点列表 final recentLocationsJson = _recentLocations .map((e) => e.toJson()) .toList(); IXSP.saveRecentLocations(recentLocationsJson); } catch (e) { log(TAG, 'Error saving locations to storage: $e'); } } void onRefresh() async { try { await apiController.refreshLaunch(); getBanner(position: 'nine', isCache: false); getBanner(position: 'banner', isCache: false); refreshController.refreshCompleted(); } catch (e) { refreshController.refreshFailed(); } } /// 当 Launch 数据更新时刷新节点 void refreshOnLaunchChanged() { log(TAG, 'Launch data changed, refreshing locations'); _loadSavedLocations(); } // 设置默认auto连接 void setDefaultAutoConnect() { coreController.locationSelectionType = 'auto'; coreController.handleConnection(); } /// 获取 banner 列表 /// [position] banner 位置类型,如 "banner"、"media"、"nine" 等 Future getBanner({ String position = 'banner', bool isCache = true, }) async { try { // 先读取缓存数据 final cacheBanners = IXSP.getBanner(position); if (cacheBanners != null && cacheBanners.list != null && cacheBanners.list!.isNotEmpty && isCache) { if (position == 'banner') { bannerList = cacheBanners.list!; } else if (position == 'nine') { nineBannerList = cacheBanners.list!; } log(TAG, 'Loaded banner from cache for position: $position'); } // 请求最新数据 final banners = await apiController.getBanner(position: position); if (banners.list != null) { if (position == 'banner') { bannerList = banners.list!; } else if (position == 'nine') { nineBannerList = banners.list!; } // 保存到缓存 await IXSP.saveBanner(position, banners); log(TAG, 'Banner updated and cached for position: $position'); } } catch (e) { log(TAG, 'Error loading banners for position $position: $e'); } } //点击banner void onBannerTap(Banner? banner) { if (banner?.action == null) return; final action = BannerAction.values.firstWhere( (e) => e.toString().split('.').last == banner?.action, orElse: () => BannerAction.notice, // 默认值 ); switch (action) { case BannerAction.urlOut: if (banner?.data != null) { SystemHelper.openWebPage(banner!.data!); } break; case BannerAction.urlIn: if (banner?.data != null) { Get.toNamed( Routes.WEB, arguments: { 'title': banner?.title ?? '', 'url': banner?.data ?? '', }, ); } break; case BannerAction.deepLink: if (banner?.data != null) { // Handle deep link SystemHelper.handleDeepLink(banner!.data!); } break; case BannerAction.openPkg: if (banner?.data != null) { // Open specific package SystemHelper.openPackage(banner!.data!); } break; case BannerAction.notice: if (banner?.data != null) { // Show notice dialog ErrorDialog.show( title: banner?.title ?? '', message: banner?.content, ); } break; case BannerAction.page: if (banner?.data != null) { final uri = Uri.parse(banner!.data!); Get.toNamed( uri.path, arguments: {...uri.queryParameters, 'banner': banner}, ); } break; } } }