@@ -42,6 +42,9 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
4242 _handleNotificationDisconnect ();
4343 });
4444
45+ // Load configurations first
46+ await loadConfigs ();
47+
4548 // Load subscriptions
4649 await loadSubscriptions ();
4750
@@ -53,31 +56,40 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
5356 // If we have a default subscription URL, load it
5457 print ('Loading default subscription URL: $defaultSubscriptionUrl ' );
5558
56- // If we don't have any subscriptions or the URL is different from existing ones
57- bool shouldAddSubscription = true ;
59+ // Check for any subscriptions named 'Default Subscription'
60+ List <Subscription > defaultSubscriptions = _subscriptions
61+ .where ((sub) => sub.name == 'Default Subscription' || sub.name == 'Default' )
62+ .toList ();
5863
59- if (_subscriptions.isNotEmpty) {
60- // Check if we already have this subscription
61- for (var subscription in _subscriptions) {
62- if (subscription.url == defaultSubscriptionUrl) {
63- // We already have this subscription, just update it
64- await updateSubscription (subscription);
65- shouldAddSubscription = false ;
66- break ;
67- }
64+ // If we have any default subscriptions
65+ if (defaultSubscriptions.isNotEmpty) {
66+ // Keep only the first one and remove all others
67+ Subscription defaultSubscription = defaultSubscriptions.first;
68+
69+ // Remove all other default subscriptions
70+ for (int i = 1 ; i < defaultSubscriptions.length; i++ ) {
71+ await removeSubscription (defaultSubscriptions[i]);
6872 }
6973
70- // If we have other subscriptions but not this one, remove them and add this one
71- if (shouldAddSubscription) {
72- // Remove all existing subscriptions
73- for (var subscription in List <Subscription >.from (_subscriptions)) {
74- await removeSubscription (subscription);
75- }
74+ // Ensure the name is consistent
75+ if (defaultSubscription.name != 'Default Subscription' ) {
76+ defaultSubscription = defaultSubscription.copyWith (name: 'Default Subscription' );
77+ await updateSubscriptionInfo (defaultSubscription);
7678 }
77- }
78-
79- // Add the default subscription if needed
80- if (shouldAddSubscription) {
79+
80+ // Update URL if needed
81+ if (defaultSubscription.url != defaultSubscriptionUrl) {
82+ final updatedSubscription = defaultSubscription.copyWith (
83+ url: defaultSubscriptionUrl
84+ );
85+ await updateSubscriptionInfo (updatedSubscription);
86+ await updateSubscription (updatedSubscription);
87+ } else {
88+ // URL is the same, just update the subscription
89+ await updateSubscription (defaultSubscription);
90+ }
91+ } else {
92+ // No default subscription found, add one
8193 await addSubscription ('Default Subscription' , defaultSubscriptionUrl);
8294 }
8395 } else {
@@ -198,6 +210,32 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
198210 _setLoading (true );
199211 try {
200212 _subscriptions = await _v2rayService.loadSubscriptions ();
213+
214+ // Ensure configs are loaded and match subscription config IDs
215+ if (_configs.isEmpty) {
216+ _configs = await _v2rayService.loadConfigs ();
217+ }
218+
219+ // Verify that all subscription config IDs exist in the configs list
220+ // If not, it means the configs weren't properly saved or loaded
221+ for (var subscription in _subscriptions) {
222+ final configIds = subscription.configIds;
223+ final existingConfigIds = _configs.map ((c) => c.id).toSet ();
224+
225+ // Check if any config IDs in the subscription are missing from the configs list
226+ final missingConfigIds = configIds.where ((id) => ! existingConfigIds.contains (id)).toList ();
227+
228+ if (missingConfigIds.isNotEmpty) {
229+ print ('Warning: Found ${missingConfigIds .length } missing configs for subscription ${subscription .name }' );
230+ // Update the subscription to remove missing config IDs
231+ final updatedConfigIds = configIds.where ((id) => existingConfigIds.contains (id)).toList ();
232+ final index = _subscriptions.indexWhere ((s) => s.id == subscription.id);
233+ if (index != - 1 ) {
234+ _subscriptions[index] = subscription.copyWith (configIds: updatedConfigIds);
235+ }
236+ }
237+ }
238+
201239 notifyListeners ();
202240 } catch (e) {
203241 _setError ('Failed to load subscriptions: $e ' );
@@ -246,18 +284,8 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
246284 // Add configs and display them immediately
247285 _configs.addAll (configs);
248286
249- // Save configs and update UI immediately to show servers
250- await _v2rayService.saveConfigs (_configs);
251- _setLoading (false );
252- notifyListeners ();
253-
254287 final newConfigIds = configs.map ((c) => c.id).toList ();
255288
256- // Server delay functionality removed as requested
257-
258- // Save configs
259- await _v2rayService.saveConfigs (_configs);
260-
261289 // Create subscription
262290 final subscription = Subscription (
263291 id: DateTime .now ().millisecondsSinceEpoch.toString (),
@@ -268,9 +296,32 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
268296 );
269297
270298 _subscriptions.add (subscription);
299+
300+ // Save both configs and subscription
301+ await _v2rayService.saveConfigs (_configs);
271302 await _v2rayService.saveSubscriptions (_subscriptions);
303+
304+ // Update UI after everything is saved
305+ notifyListeners ();
272306 } catch (e) {
273- _setError ('Failed to add subscription: $e ' );
307+ String errorMsg = 'Failed to add subscription' ;
308+
309+ // Provide more specific error messages
310+ if (e.toString ().contains ('Network error' ) ||
311+ e.toString ().contains ('timeout' ) ||
312+ e.toString ().contains ('SocketException' )) {
313+ errorMsg = 'Network error: Check your internet connection' ;
314+ } else if (e.toString ().contains ('Invalid URL' )) {
315+ errorMsg = 'Invalid subscription URL format' ;
316+ } else if (e.toString ().contains ('No valid servers' )) {
317+ errorMsg = 'No valid servers found in subscription' ;
318+ } else if (e.toString ().contains ('HTTP' )) {
319+ errorMsg = 'Server error: ${e .toString ()}' ;
320+ } else {
321+ errorMsg = 'Failed to add subscription: ${e .toString ()}' ;
322+ }
323+
324+ _setError (errorMsg);
274325 } finally {
275326 _setLoading (false );
276327 }
@@ -297,14 +348,6 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
297348 // Add new configs and display them immediately
298349 _configs.addAll (configs);
299350
300- // Save configs and update UI immediately to show servers
301- await _v2rayService.saveConfigs (_configs);
302-
303- // Mark loading as complete
304- _isLoadingServers = false ;
305- _setLoading (false );
306- notifyListeners ();
307-
308351 final newConfigIds = configs.map ((c) => c.id).toList ();
309352
310353 // Update subscription
@@ -314,14 +357,147 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
314357 lastUpdated: DateTime .now (),
315358 configIds: newConfigIds,
316359 );
360+
361+ // Save both configs and subscriptions to ensure persistence
362+ await _v2rayService.saveConfigs (_configs);
317363 await _v2rayService.saveSubscriptions (_subscriptions);
318364 }
365+
366+ // Mark loading as complete
367+ _isLoadingServers = false ;
368+ _setLoading (false );
369+ notifyListeners ();
319370 } catch (e) {
320- _setError ('Failed to update subscription: $e ' );
371+ String errorMsg = 'Failed to update subscription' ;
372+
373+ // Provide more specific error messages
374+ if (e.toString ().contains ('Network error' ) ||
375+ e.toString ().contains ('timeout' ) ||
376+ e.toString ().contains ('SocketException' )) {
377+ errorMsg = 'Network error: Check your internet connection' ;
378+ } else if (e.toString ().contains ('Invalid URL' )) {
379+ errorMsg = 'Invalid subscription URL format' ;
380+ } else if (e.toString ().contains ('No valid servers' )) {
381+ errorMsg = 'No valid servers found in subscription' ;
382+ } else if (e.toString ().contains ('HTTP' )) {
383+ errorMsg = 'Server error: ${e .toString ()}' ;
384+ } else {
385+ errorMsg = 'Failed to update subscription: ${e .toString ()}' ;
386+ }
387+
388+ _setError (errorMsg);
321389 } finally {
322390 _setLoading (false );
323391 }
324392 }
393+
394+ // Update subscription info without refreshing servers
395+ Future <void > updateSubscriptionInfo (Subscription subscription) async {
396+ _setLoading (true );
397+ _errorMessage = '' ;
398+
399+ try {
400+ // Find and update the subscription
401+ final index = _subscriptions.indexWhere ((s) => s.id == subscription.id);
402+ if (index != - 1 ) {
403+ _subscriptions[index] = subscription;
404+ await _v2rayService.saveSubscriptions (_subscriptions);
405+ notifyListeners ();
406+ } else {
407+ _setError ('Subscription not found' );
408+ }
409+ } catch (e) {
410+ String errorMsg = 'Failed to update subscription info' ;
411+
412+ // Provide more specific error messages
413+ if (e.toString ().contains ('Network error' ) ||
414+ e.toString ().contains ('timeout' ) ||
415+ e.toString ().contains ('SocketException' )) {
416+ errorMsg = 'Network error: Check your internet connection' ;
417+ } else if (e.toString ().contains ('Invalid URL' )) {
418+ errorMsg = 'Invalid subscription URL format' ;
419+ } else if (e.toString ().contains ('Permission' )) {
420+ errorMsg = 'Permission error: Unable to save subscription' ;
421+ } else {
422+ errorMsg = 'Failed to update subscription info: ${e .toString ()}' ;
423+ }
424+
425+ _setError (errorMsg);
426+ } finally {
427+ _setLoading (false );
428+ }
429+ }
430+
431+ // Update all subscriptions
432+ Future <void > updateAllSubscriptions () async {
433+ _setLoading (true );
434+ _errorMessage = '' ;
435+ _isLoadingServers = true ;
436+ notifyListeners ();
437+
438+ try {
439+ // Make a copy to avoid modification during iteration
440+ final subscriptionsCopy = List <Subscription >.from (_subscriptions);
441+ bool anyUpdated = false ;
442+ List <String > failedSubscriptions = [];
443+
444+ for (final subscription in subscriptionsCopy) {
445+ try {
446+ // Skip empty or invalid subscriptions
447+ if (subscription.url.isEmpty) continue ;
448+
449+ final configs = await _v2rayService.parseSubscriptionUrl (subscription.url);
450+
451+ // Remove old configs for this subscription
452+ _configs.removeWhere ((c) => subscription.configIds.contains (c.id));
453+
454+ // Add new configs
455+ _configs.addAll (configs);
456+
457+ final newConfigIds = configs.map ((c) => c.id).toList ();
458+
459+ // Update subscription
460+ final index = _subscriptions.indexWhere ((s) => s.id == subscription.id);
461+ if (index != - 1 ) {
462+ _subscriptions[index] = subscription.copyWith (
463+ lastUpdated: DateTime .now (),
464+ configIds: newConfigIds,
465+ );
466+ anyUpdated = true ;
467+ }
468+ } catch (e) {
469+ // Record failed subscription
470+ failedSubscriptions.add (subscription.name);
471+ print ('Error updating subscription ${subscription .name }: $e ' );
472+ }
473+ }
474+
475+ // Save all changes at once to reduce disk operations
476+ if (anyUpdated) {
477+ await _v2rayService.saveConfigs (_configs);
478+ await _v2rayService.saveSubscriptions (_subscriptions);
479+ }
480+
481+ // Set error message if any subscriptions failed
482+ if (failedSubscriptions.isNotEmpty) {
483+ if (failedSubscriptions.length == _subscriptions.length) {
484+ // All subscriptions failed - likely a network issue
485+ _setError ('Failed to update subscriptions: Network error or invalid URLs' );
486+ } else {
487+ // Some subscriptions failed
488+ _setError ('Failed to update: ${failedSubscriptions .join (', ' )}' );
489+ }
490+ }
491+
492+ _isLoadingServers = false ;
493+ notifyListeners ();
494+ } catch (e) {
495+ _setError ('Failed to update all subscriptions: $e ' );
496+ } finally {
497+ _setLoading (false );
498+ _isLoadingServers = false ;
499+ }
500+ }
325501
326502 Future <void > removeSubscription (Subscription subscription) async {
327503 // Remove configs associated with this subscription
0 commit comments