import 'dart:async'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/core/core.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/providers/action.dart'; import 'package:fl_clash/providers/app.dart'; import 'package:fl_clash/providers/config.dart'; import 'package:fl_clash/providers/state.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class CoreManager extends ConsumerStatefulWidget { final Widget child; const CoreManager({super.key, required this.child}); @override ConsumerState createState() => _CoreContainerState(); } class _CoreContainerState extends ConsumerState with CoreEventListener { @override Widget build(BuildContext context) { return widget.child; } @override void initState() { super.initState(); coreEventManager.addListener(this); ref.listenManual(currentProfileIdProvider, (prev, next) { if (prev != next) { WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(setupActionProvider.notifier).fullSetup(); }); } }); ref.listenManual(updateParamsProvider, (prev, next) { if (prev != next) { ref.read(setupActionProvider.notifier).updateConfigDebounce(); } }); ref.listenManual(appSettingProvider.select((state) => state.openLogs), ( prev, next, ) { if (next) { coreController.startLog(); } else { coreController.stopLog(); } }, fireImmediately: true); } @override Future dispose() async { coreEventManager.removeListener(this); super.dispose(); } @override Future onDelay(Delay delay) async { super.onDelay(delay); final proxiesAction = ref.read(proxiesActionProvider.notifier); proxiesAction.setDelay(delay); debouncer.call(FunctionTag.updateDelay, () async { proxiesAction.updateGroupsDebounce(); }, duration: const Duration(milliseconds: 5000)); } @override void onLog(Log log) { ref.read(logsProvider.notifier).add(log); if (log.logLevel == LogLevel.error) { globalState.showNotifier(log.payload); } super.onLog(log); } @override void onRequest(TrackerInfo trackerInfo) async { ref.read(requestsProvider.notifier).addRequest(trackerInfo); super.onRequest(trackerInfo); } @override Future onLoaded(String providerName) async { final ref = globalState.container; ref .read(providersProvider.notifier) .setProvider(await coreController.getExternalProvider(providerName)); debouncer.call(FunctionTag.loadedProvider, () async { ref.read(proxiesActionProvider.notifier).updateGroupsDebounce(); }, duration: const Duration(milliseconds: 5000)); super.onLoaded(providerName); } @override Future onCrash(String message) async { if (ref.read(coreStatusProvider) != CoreStatus.connected) { return; } ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected; if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) { context.showNotifier(message); } await coreController.shutdown(false); super.onCrash(message); } @override void onGeoUpdate(String geoType, bool updating, bool skipped, String? error) { final geoResource = GeoResource.fromJson(geoType.toLowerCase()); final key = geoResource.updatingKey; final l10n = currentAppLocalizations; if (updating) { globalState.showNotifier(l10n.geoUpdating(geoResource.name)); } else if (skipped) { globalState.showNotifier(l10n.geoSkipped(geoResource.name)); } else { globalState.showNotifier(l10n.geoUpdated(geoResource.name)); } ref.read(isUpdatingProvider(key).notifier).value = updating; if (!updating && error != null && error.isNotEmpty) { globalState.showNotifier(error); } super.onGeoUpdate(geoType, updating, skipped, error); } }