import 'package:dartx/dartx.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/widget/animated_visibility.dart'; import 'package:hiddify/core/widget/shimmer_skeleton.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/proxy/active/ip_widget.dart'; import 'package:hiddify/features/proxy/model/proxy_failure.dart'; import 'package:hiddify/features/stats/notifier/stats_notifier.dart'; import 'package:hiddify/gen/fonts.gen.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ActiveProxyFooter extends HookConsumerWidget { const ActiveProxyFooter({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider); final theme = Theme.of(context); final activeProxy = ref.watch(activeProxyNotifierProvider); final ipInfo = ref.watch(ipInfoNotifierProvider); final stats = ref.watch(statsNotifierProvider).value; return AnimatedVisibility( axis: Axis.vertical, visible: activeProxy is AsyncData, child: switch (activeProxy) { AsyncData(value: final proxy) => Padding( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: Column( children: [ // Карточка с информацией о прокси и локации Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Название прокси Row( children: [ Icon( FluentIcons.arrow_routing_20_regular, size: 20, color: theme.colorScheme.primary, ), const Gap(12), Expanded( child: Text( proxy.selectedName.isNotNullOrBlank ? proxy.selectedName! : proxy.name, semanticsLabel: t.proxies.activeProxySemanticLabel, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, fontFamily: FontFamily.emoji, ), overflow: TextOverflow.ellipsis, ), ), ], ), const Gap(12), const Divider(height: 1), const Gap(12), // Информация о стране switch (ipInfo) { AsyncData(value: final info) => InkWell( onTap: () async { ref.read(ipInfoNotifierProvider.notifier).refresh(); }, borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ IPCountryFlag(countryCode: info.countryCode), const Gap(12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( info.countryCode.toUpperCase(), style: theme.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, ), ), if (info.org?.isNotEmpty ?? false) Text( info.org!, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ), ), AsyncError(error: final UnknownIp _) => InkWell( onTap: () async { ref.read(ipInfoNotifierProvider.notifier).refresh(); }, borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ const Icon(FluentIcons.arrow_sync_20_regular), const Gap(12), Text( t.proxies.checkIp, style: theme.textTheme.bodyMedium, ), ], ), ), ), AsyncError() => InkWell( onTap: () async { ref.read(ipInfoNotifierProvider.notifier).refresh(); }, borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ Icon( FluentIcons.error_circle_20_regular, color: theme.colorScheme.error, ), const Gap(12), Text( t.proxies.unknownIp, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.error, ), ), ], ), ), ), _ => const Padding( padding: EdgeInsets.symmetric(vertical: 4), child: Row( children: [ Icon(FluentIcons.question_circle_20_regular), Gap(12), Expanded( child: ShimmerSkeleton(height: 16, widthFactor: 0.6), ), ], ), ), }, ], ), ), ), const Gap(12), // Карточки со статистикой Row( children: [ Expanded( child: Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( FluentIcons.arrow_bidirectional_up_down_20_regular, size: 24, color: theme.colorScheme.secondary, ), const Gap(8), Text( (stats?.downlinkTotal ?? 0).size(), semanticsLabel: t.stats.totalTransferred, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), Text( t.stats.totalTransferred, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ), ), ), const Gap(12), Expanded( child: Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( FluentIcons.arrow_download_20_regular, size: 24, color: theme.colorScheme.tertiary, ), const Gap(8), Text( (stats?.downlink ?? 0).speed(), semanticsLabel: t.stats.speed, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), Text( t.stats.speed, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ), ), ), ], ), ], ), ), _ => const SizedBox(), }, ); } } class _InfoProp extends StatelessWidget { const _InfoProp({ required this.icon, required this.text, this.semanticLabel, }); final IconData icon; final String text; final String? semanticLabel; @override Widget build(BuildContext context) { return Semantics( label: semanticLabel, child: Row( children: [ Icon(icon), const Gap(8), Flexible( child: Text( text, style: Theme.of(context) .textTheme .labelMedium ?.copyWith(fontFamily: FontFamily.emoji), overflow: TextOverflow.ellipsis, ), ), ], ), ); } }