import 'package:hiddify/core/prefs/prefs.dart'; import 'package:hiddify/data/data_providers.dart'; import 'package:hiddify/domain/connectivity/connectivity.dart'; import 'package:hiddify/domain/core_facade.dart'; import 'package:hiddify/features/common/active_profile/active_profile_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:rxdart/rxdart.dart'; part 'connectivity_controller.g.dart'; @Riverpod(keepAlive: true) class ConnectivityController extends _$ConnectivityController with AppLogger { @override Stream build() { ref.listen( activeProfileProvider.select((value) => value.asData?.value), (previous, next) async { if (previous == null) return; final shouldReconnect = previous != next; if (shouldReconnect) { await reconnect(next?.id); } }, ); return _core.watchConnectionStatus().doOnData((event) { loggy.info("connection status: ${event.format()}"); }); } CoreFacade get _core => ref.watch(coreFacadeProvider); Future toggleConnection() async { if (state case AsyncError()) { await _connect(); } else if (state case AsyncData(:final value)) { switch (value) { case Disconnected(): await _connect(); case Connected(): await _disconnect(); default: loggy.warning("switching status, debounce"); } } } Future reconnect(String? profileId) async { if (state case AsyncData(:final value) when value == const Connected()) { if (profileId == null) { loggy.info("no active profile, disconnecting"); return _disconnect(); } loggy.info("active profile changed, reconnecting"); await _core .restart(profileId, ref.read(disableMemoryLimitProvider)) .mapLeft((err) { loggy.warning("error reconnecting", err); state = AsyncError(err, StackTrace.current); }).run(); } } Future abortConnection() async { if (state case AsyncData(:final value)) { switch (value) { case Connected() || Connecting(): loggy.debug("aborting connection"); await _disconnect(); default: } } } Future _connect() async { final activeProfile = await ref.read(activeProfileProvider.future); await _core .start(activeProfile!.id, ref.read(disableMemoryLimitProvider)) .mapLeft((err) { loggy.warning("error connecting $err", err); state = AsyncError(err, StackTrace.current); }).run(); } Future _disconnect() async { await _core.stop().mapLeft((err) { loggy.warning("error disconnecting", err); state = AsyncError(err, StackTrace.current); }).run(); } } @Riverpod(keepAlive: true) Future serviceRunning(ServiceRunningRef ref) => ref .watch( connectivityControllerProvider.selectAsync((data) => data.isConnected), ) .onError((error, stackTrace) => false);