lilu 6 місяців тому
батько
коміт
9bfda18b96

+ 5 - 5
lib/app/constants/enums.dart

@@ -85,7 +85,7 @@ enum MemberLevel {
   }
 }
 
-enum VpnError {
+enum VpnStatus {
   idle(0, '空闲状态'), // 空闲状态
   connecting(1, '连接中状态'), // 连接中状态
   connected(2, '连接成功状态'), // 连接成功状态
@@ -96,12 +96,12 @@ enum VpnError {
   final int value;
   final String label;
 
-  const VpnError(this.value, this.label);
+  const VpnStatus(this.value, this.label);
 
-  static VpnError fromValue(int value) {
-    return VpnError.values.firstWhere(
+  static VpnStatus fromValue(int value) {
+    return VpnStatus.values.firstWhere(
       (e) => e.value == value,
-      orElse: () => VpnError.idle,
+      orElse: () => VpnStatus.idle,
     );
   }
 }

+ 24 - 10
lib/app/controllers/core_controller.dart

@@ -10,6 +10,7 @@ import '../../utils/haptic_feedback_manager.dart';
 import '../../utils/log/logger.dart';
 import '../constants/enums.dart';
 import '../data/models/vpn_message.dart';
+import '../data/sp/ix_sp.dart';
 import '../dialog/error_dialog.dart';
 import '../dialog/feedback_bottom_sheet.dart';
 import 'api_controller.dart';
@@ -68,6 +69,19 @@ class CoreController extends GetxService {
     }
   }
 
+  void selectLocationConnect() {
+    if (state != ConnectionState.disconnected) {
+      CoreApi().disconnect();
+      // 延迟300ms
+      Future.delayed(const Duration(milliseconds: 300), () {
+        state = ConnectionState.connecting;
+        getDispatchInfo();
+      });
+    } else {
+      handleConnection();
+    }
+  }
+
   Future<void> getDispatchInfo() async {
     // 如果正在请求中,取消当前请求
     if (_cancelToken != null) {
@@ -80,8 +94,8 @@ class CoreController extends GetxService {
     _cancelToken = currentToken;
 
     try {
-      final locationId = 187;
-      final locationCode = 'auto_mm';
+      final locationId = IXSP.getSelectedLocation()?['id'];
+      final locationCode = IXSP.getSelectedLocation()?['code'];
       final launch = await _apiController.getDispatchInfo(
         locationId,
         locationCode,
@@ -161,31 +175,31 @@ class CoreController extends GetxService {
   }
 
   void _handleVpnStatus(VpnStatusMessage message) {
-    final vpnError = VpnError.fromValue(message.status);
+    final vpnError = VpnStatus.fromValue(message.status);
     log(TAG, 'VPN状态变化: ${vpnError.label}, message=${message.message}');
     // 根据状态码处理不同的VPN状态
     switch (vpnError) {
-      case VpnError.idle:
+      case VpnStatus.idle:
         // disconnected
         _onVpnDisconnected();
         break;
-      case VpnError.connecting:
+      case VpnStatus.connecting:
         // connecting
         _onVpnConnecting();
         break;
-      case VpnError.connected:
+      case VpnStatus.connected:
         // connected
         _onVpnConnected();
         break;
-      case VpnError.error:
+      case VpnStatus.error:
         // error
         _onVpnError();
         break;
-      case VpnError.serviceDisconnected:
+      case VpnStatus.serviceDisconnected:
         // service disconnected
         _onVpnServiceDisconnected();
         break;
-      case VpnError.permissionDenied:
+      case VpnStatus.permissionDenied:
         // permission denied
         _onVpnPermissionDenied();
         break;
@@ -219,7 +233,7 @@ class CoreController extends GetxService {
     state = ConnectionState.disconnected;
     timer = "00:00:00";
     HapticFeedbackManager.connectionDisconnected();
-    FeedbackBottomSheet.show();
+    // FeedbackBottomSheet.show();
   }
 
   void _onVpnConnecting() {

+ 64 - 0
lib/app/data/sp/ix_sp.dart

@@ -249,6 +249,70 @@ class IXSP {
   static int getEnablePingMode() =>
       _sharedPreferences.getInt(_enablePingModeKey) ?? 0;
 
+  //记录当前选中的节点
+  static const String _selectedLocationKey = 'selected_location';
+  //记录最近选择的节点列表(最多3个)
+  static const String _recentLocationsKey = 'recent_locations';
+
+  /// 保存当前选中的节点
+  static Future<void> saveSelectedLocation(
+    Map<String, dynamic> location,
+  ) async {
+    try {
+      await _sharedPreferences.setString(
+        _selectedLocationKey,
+        jsonEncode(location),
+      );
+      log('IXSP', 'Selected location saved successfully');
+    } catch (e) {
+      log('IXSP', 'Error saving selected location: $e');
+    }
+  }
+
+  /// 获取当前选中的节点
+  static Map<String, dynamic>? getSelectedLocation() {
+    try {
+      final jsonData = _sharedPreferences.getString(_selectedLocationKey);
+      if (jsonData == null) {
+        return null;
+      }
+      return jsonDecode(jsonData) as Map<String, dynamic>;
+    } catch (e) {
+      log('IXSP', 'Error getting selected location: $e');
+      return null;
+    }
+  }
+
+  /// 保存最近选择的节点列表
+  static Future<void> saveRecentLocations(
+    List<Map<String, dynamic>> locations,
+  ) async {
+    try {
+      await _sharedPreferences.setString(
+        _recentLocationsKey,
+        jsonEncode(locations),
+      );
+      log('IXSP', 'Recent locations saved successfully');
+    } catch (e) {
+      log('IXSP', 'Error saving recent locations: $e');
+    }
+  }
+
+  /// 获取最近选择的节点列表
+  static List<Map<String, dynamic>> getRecentLocations() {
+    try {
+      final jsonData = _sharedPreferences.getString(_recentLocationsKey);
+      if (jsonData == null || jsonData.isEmpty) {
+        return [];
+      }
+      final List<dynamic> list = jsonDecode(jsonData);
+      return list.map((e) => e as Map<String, dynamic>).toList();
+    } catch (e) {
+      log('IXSP', 'Error getting recent locations: $e');
+      return [];
+    }
+  }
+
   /// 保存 Launch 数据
   static Future<bool> saveLaunch(Launch launch) async {
     try {

+ 74 - 44
lib/app/modules/home/controllers/home_controller.dart

@@ -1,7 +1,9 @@
 import 'package:get/get.dart';
+import '../../../../utils/log/logger.dart';
 import '../../../base/base_controller.dart';
 import '../../../controllers/core_controller.dart';
 import '../../../data/models/launch/groups.dart';
+import '../../../data/sp/ix_sp.dart';
 
 /// 主页控制器
 class HomeController extends BaseController {
@@ -45,10 +47,6 @@ class HomeController extends BaseController {
   final _recentLocations = <Locations>[].obs;
   List<Locations> get recentLocations => _recentLocations;
 
-  // 所有可用位置列表
-  final _allLocations = <Locations>[].obs;
-  List<Locations> get allLocations => _allLocations;
-
   // 是否是会员
   final _isPremium = false.obs;
   bool get isPremium => _isPremium.value;
@@ -62,43 +60,55 @@ class HomeController extends BaseController {
 
   /// 初始化位置数据
   void _initializeLocations() {
-    // 模拟数据 - 实际应用中应该从API获取
-    _allLocations.addAll([
-      const Locations(
-        id: 1,
-        name: 'LosAngles',
-        code: 'US',
-        country: 'United States',
-        latency: 15,
-      ),
-      const Locations(
-        id: 2,
-        name: 'Singapore',
-        code: 'SG',
-        country: 'Singapore',
-        latency: 25,
-      ),
-      const Locations(
-        id: 3,
-        name: 'Japan',
-        code: 'JP',
-        country: 'Japan',
-        latency: 35,
-      ),
-      const Locations(
-        id: 4,
-        name: 'Madrid',
-        code: 'ES',
-        country: 'Spain',
-        latency: 30,
-      ),
-    ]);
-
-    // 设置默认选择的位置
-    selectedLocation = _allLocations.first;
-
-    // 设置最近使用的位置
-    _recentLocations.addAll(_allLocations.skip(1).take(3));
+    // 从 SharedPreferences 加载保存的节点数据
+    _loadSavedLocations();
+  }
+
+  /// 从 SharedPreferences 加载保存的节点数据
+  void _loadSavedLocations() {
+    try {
+      // 加载当前选中的节点
+      final selectedLocationData = IXSP.getSelectedLocation();
+      if (selectedLocationData != null) {
+        selectedLocation = Locations.fromJson(selectedLocationData);
+      } 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');
+    }
+  }
+
+  /// 选中第一个可用节点
+  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');
+    }
   }
 
   /// 选择位置
@@ -107,6 +117,13 @@ class HomeController extends BaseController {
 
     // 更新最近使用列表
     _updateRecentLocations(location);
+
+    // 保存到 SharedPreferences
+    _saveLocationsToStorage();
+  }
+
+  void handleConnect() {
+    coreController.selectLocationConnect();
   }
 
   /// 更新最近使用的位置列表
@@ -123,8 +140,21 @@ class HomeController extends BaseController {
     }
   }
 
-  /// 判断位置是否快速
-  bool isLocationFast(Locations location) {
-    return location.latency != null && location.latency! < 30;
+  /// 保存位置数据到 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');
+    }
   }
 }

+ 4 - 6
lib/app/modules/home/views/home_view.dart

@@ -133,8 +133,6 @@ class HomeView extends BaseView<HomeController> {
   Widget _buildSelectedLocationCard() {
     return GestureDetector(
       onTap: () {
-        // 打开位置选择弹窗
-        // showLocationSelectionModal();
         Get.toNamed(Routes.NODE);
       },
       child: Obx(() {
@@ -153,7 +151,7 @@ class HomeView extends BaseView<HomeController> {
                 borderRadius: BorderRadius.circular(4.r), // 设置圆角
                 child: SvgPicture.asset(
                   Assets.getCountryFlagImage(
-                    controller.selectedLocation!.code!,
+                    controller.selectedLocation?.country ?? '',
                   ),
                   width: 32.w,
                   height: 24.w,
@@ -179,7 +177,7 @@ class HomeView extends BaseView<HomeController> {
               // 位置名称
               Expanded(
                 child: Text(
-                  '${controller.selectedLocation!.code} - ${controller.selectedLocation!.name}',
+                  '${controller.selectedLocation?.code ?? ''} - ${controller.selectedLocation?.name ?? ''}',
                   style: TextStyle(
                     fontSize: 16.sp,
                     height: 1.5,
@@ -266,7 +264,7 @@ class HomeView extends BaseView<HomeController> {
                                     borderRadius: BorderRadius.circular(4.r),
                                     child: SvgPicture.asset(
                                       Assets.getCountryFlagImage(
-                                        location.code!,
+                                        location.country ?? '',
                                       ),
                                       width: 24.w,
                                       height: 16.w,
@@ -351,7 +349,7 @@ class HomeView extends BaseView<HomeController> {
                                       ), // 设置圆角
                                       child: SvgPicture.asset(
                                         Assets.getCountryFlagImage(
-                                          location.code!,
+                                          location.country ?? '',
                                         ),
                                         width: 28.w,
                                         height: 21.w,

+ 7 - 1
lib/app/modules/node/widgets/node_list.dart

@@ -8,6 +8,7 @@ import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
 
 import '../../../constants/assets.dart';
 import '../../../data/models/launch/groups.dart';
+import '../../home/controllers/home_controller.dart';
 import '../controllers/node_controller.dart';
 
 class NodeList extends StatefulWidget {
@@ -200,7 +201,12 @@ class _CountrySection extends StatelessWidget {
 
                     return ClickOpacity(
                       onTap: () {
-                        // TODO: 处理节点选择
+                        // 获取 HomeController 并保存选中的节点
+                        final homeController = Get.find<HomeController>();
+                        homeController.selectLocation(location);
+                        homeController.handleConnect();
+                        // 返回上一页
+                        Get.back();
                       },
                       child: Column(
                         children: [