diff --git a/lib/features/proxy/active/active_proxy_footer.dart b/lib/features/proxy/active/active_proxy_footer.dart index ef0679cc..ab1c305c 100644 --- a/lib/features/proxy/active/active_proxy_footer.dart +++ b/lib/features/proxy/active/active_proxy_footer.dart @@ -19,8 +19,10 @@ class ActiveProxyFooter extends HookConsumerWidget { @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, @@ -28,81 +30,215 @@ class ActiveProxyFooter extends HookConsumerWidget { child: switch (activeProxy) { AsyncData(value: final proxy) => Padding( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( children: [ - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _InfoProp( - icon: FluentIcons.arrow_routing_20_regular, - text: proxy.selectedName.isNotNullOrBlank - ? proxy.selectedName! - : proxy.name, - semanticLabel: t.proxies.activeProxySemanticLabel, - ), - const Gap(8), - switch (ipInfo) { - AsyncData(value: final info) => Row( - children: [ - IPCountryFlag(countryCode: info.countryCode), - const Gap(8), - IPText( - ip: info.ip, - onLongPress: () async { - ref - .read(ipInfoNotifierProvider.notifier) - .refresh(); - }, + // Карточка с информацией о прокси и локации + 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, ), - ], - ), - AsyncError(error: final UnknownIp _) => Row( - children: [ - const Icon(FluentIcons.arrow_sync_20_regular), - const Gap(8), - UnknownIPText( - text: t.proxies.checkIp, - onTap: () async { - ref - .read(ipInfoNotifierProvider.notifier) - .refresh(); - }, + ), + ], + ), + 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() => Row( - children: [ - const Icon(FluentIcons.error_circle_20_regular), - const Gap(8), - UnknownIPText( - text: t.proxies.unknownIp, - onTap: () async { - ref - .read(ipInfoNotifierProvider.notifier) - .refresh(); - }, + ), + 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, + ), + ], + ), ), - ], - ), - _ => const Row( + ), + 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.question_circle_20_regular), - Gap(8), - Flexible( - child: ShimmerSkeleton( - height: 16, - widthFactor: 1, + 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 _StatsColumn(), ], ), ), @@ -112,38 +248,6 @@ class ActiveProxyFooter extends HookConsumerWidget { } } -class _StatsColumn extends HookConsumerWidget { - const _StatsColumn(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final t = ref.watch(translationsProvider); - final stats = ref.watch(statsNotifierProvider).value; - - return Directionality( - textDirection: TextDirection.values[ - (Directionality.of(context).index + 1) % TextDirection.values.length], - child: Flexible( - child: Column( - children: [ - _InfoProp( - icon: FluentIcons.arrow_bidirectional_up_down_20_regular, - text: (stats?.downlinkTotal ?? 0).size(), - semanticLabel: t.stats.totalTransferred, - ), - const Gap(8), - _InfoProp( - icon: FluentIcons.arrow_download_20_regular, - text: (stats?.downlink ?? 0).speed(), - semanticLabel: t.stats.speed, - ), - ], - ), - ), - ); - } -} - class _InfoProp extends StatelessWidget { const _InfoProp({ required this.icon,