connection button by proxy status indicator, change connected to connecting when timeout

This commit is contained in:
hiddify-com
2024-08-04 16:46:51 +02:00
parent 7a697acf7d
commit 92bf0fa6cf
4 changed files with 35 additions and 51 deletions

View File

@@ -11,6 +11,7 @@ import 'package:hiddify/features/connection/model/connection_status.dart';
import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart';
import 'package:hiddify/features/connection/widget/experimental_feature_notice.dart'; import 'package:hiddify/features/connection/widget/experimental_feature_notice.dart';
import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart';
import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart';
import 'package:hiddify/gen/assets.gen.dart'; import 'package:hiddify/gen/assets.gen.dart';
import 'package:hiddify/utils/alerts.dart'; import 'package:hiddify/utils/alerts.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -23,8 +24,10 @@ class ConnectionButton extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final t = ref.watch(translationsProvider); final t = ref.watch(translationsProvider);
final connectionStatus = ref.watch(connectionNotifierProvider); final connectionStatus = ref.watch(connectionNotifierProvider);
final requiresReconnect = final activeProxy = ref.watch(activeProxyNotifierProvider);
ref.watch(configOptionNotifierProvider).valueOrNull; final delay = activeProxy.valueOrNull?.urlTestDelay ?? 0;
final requiresReconnect = ref.watch(configOptionNotifierProvider).valueOrNull;
final today = DateTime.now(); final today = DateTime.now();
ref.listen( ref.listen(
@@ -33,10 +36,8 @@ class ConnectionButton extends HookConsumerWidget {
if (next case AsyncError(:final error)) { if (next case AsyncError(:final error)) {
CustomAlertDialog.fromErr(t.presentError(error)).show(context); CustomAlertDialog.fromErr(t.presentError(error)).show(context);
} }
if (next if (next case AsyncData(value: Disconnected(:final connectionFailure?))) {
case AsyncData(value: Disconnected(:final connectionFailure?))) { CustomAlertDialog.fromErr(t.presentError(connectionFailure)).show(context);
CustomAlertDialog.fromErr(t.presentError(connectionFailure))
.show(context);
} }
}, },
); );
@@ -47,8 +48,7 @@ class ConnectionButton extends HookConsumerWidget {
final hasExperimental = ref.read(ConfigOptions.hasExperimentalFeatures); final hasExperimental = ref.read(ConfigOptions.hasExperimentalFeatures);
final canShowNotice = !ref.read(disableExperimentalFeatureNoticeProvider); final canShowNotice = !ref.read(disableExperimentalFeatureNoticeProvider);
if (hasExperimental && canShowNotice && context.mounted) { if (hasExperimental && canShowNotice && context.mounted) {
return await const ExperimentalFeatureNoticeDialog().show(context) ?? return await const ExperimentalFeatureNoticeDialog().show(context) ?? false;
false;
} }
return true; return true;
} }
@@ -57,52 +57,40 @@ class ConnectionButton extends HookConsumerWidget {
onTap: switch (connectionStatus) { onTap: switch (connectionStatus) {
AsyncData(value: Disconnected()) || AsyncError() => () async { AsyncData(value: Disconnected()) || AsyncError() => () async {
if (await showExperimentalNotice()) { if (await showExperimentalNotice()) {
return await ref return await ref.read(connectionNotifierProvider.notifier).toggleConnection();
.read(connectionNotifierProvider.notifier)
.toggleConnection();
} }
}, },
AsyncData(value: Connected()) => () async { AsyncData(value: Connected()) => () async {
if (requiresReconnect == true && await showExperimentalNotice()) { if (requiresReconnect == true && await showExperimentalNotice()) {
return await ref return await ref.read(connectionNotifierProvider.notifier).reconnect(await ref.read(activeProfileProvider.future));
.read(connectionNotifierProvider.notifier)
.reconnect(await ref.read(activeProfileProvider.future));
} }
return await ref return await ref.read(connectionNotifierProvider.notifier).toggleConnection();
.read(connectionNotifierProvider.notifier)
.toggleConnection();
}, },
_ => () {}, _ => () {},
}, },
enabled: switch (connectionStatus) { enabled: switch (connectionStatus) {
AsyncData(value: Connected()) || AsyncData(value: Connected()) || AsyncData(value: Disconnected()) || AsyncError() => true,
AsyncData(value: Disconnected()) ||
AsyncError() =>
true,
_ => false, _ => false,
}, },
label: switch (connectionStatus) { label: switch (connectionStatus) {
AsyncData(value: Connected()) when requiresReconnect == true => AsyncData(value: Connected()) when requiresReconnect == true => t.connection.reconnect,
t.connection.reconnect, AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => t.connection.connecting,
AsyncData(value: final status) => status.present(t), AsyncData(value: final status) => status.present(t),
_ => "", _ => "",
}, },
buttonColor: switch (connectionStatus) { buttonColor: switch (connectionStatus) {
AsyncData(value: Connected()) when requiresReconnect == true => AsyncData(value: Connected()) when requiresReconnect == true => Colors.teal,
Colors.teal, AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => Color.fromARGB(255, 185, 176, 103),
AsyncData(value: Connected()) => buttonTheme.connectedColor!, AsyncData(value: Connected()) => buttonTheme.connectedColor!,
AsyncData(value: _) => buttonTheme.idleColor!, AsyncData(value: _) => buttonTheme.idleColor!,
_ => Colors.red, _ => Colors.red,
}, },
image: switch (connectionStatus) { image: switch (connectionStatus) {
AsyncData(value: Connected()) when requiresReconnect == true => AsyncData(value: Connected()) when requiresReconnect == true => Assets.images.disconnectNorouz,
Assets.images.disconnectNorouz,
AsyncData(value: Connected()) => Assets.images.connectNorouz, AsyncData(value: Connected()) => Assets.images.connectNorouz,
AsyncData(value: _) => Assets.images.disconnectNorouz, AsyncData(value: _) => Assets.images.disconnectNorouz,
_ => Assets.images.disconnectNorouz, _ => Assets.images.disconnectNorouz,
AsyncData(value: Disconnected()) || AsyncData(value: Disconnected()) || AsyncError() => Assets.images.disconnectNorouz,
AsyncError() =>
Assets.images.disconnectNorouz,
AsyncData(value: Connected()) => Assets.images.connectNorouz, AsyncData(value: Connected()) => Assets.images.connectNorouz,
_ => Assets.images.disconnectNorouz, _ => Assets.images.disconnectNorouz,
}, },
@@ -177,9 +165,7 @@ class _ConnectionButton extends StatelessWidget {
), ),
), ),
).animate(target: enabled ? 0 : 1).blurXY(end: 1), ).animate(target: enabled ? 0 : 1).blurXY(end: 1),
) ).animate(target: enabled ? 0 : 1).scaleXY(end: .88, curve: Curves.easeIn),
.animate(target: enabled ? 0 : 1)
.scaleXY(end: .88, curve: Curves.easeIn),
), ),
const Gap(16), const Gap(16),
ExcludeSemantics( ExcludeSemantics(

View File

@@ -12,6 +12,7 @@ import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart';
import 'package:hiddify/features/profile/widget/profile_tile.dart'; import 'package:hiddify/features/profile/widget/profile_tile.dart';
import 'package:hiddify/features/proxy/active/active_proxy_delay_indicator.dart'; import 'package:hiddify/features/proxy/active/active_proxy_delay_indicator.dart';
import 'package:hiddify/features/proxy/active/active_proxy_footer.dart'; import 'package:hiddify/features/proxy/active/active_proxy_footer.dart';
import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart';
import 'package:hiddify/utils/utils.dart'; import 'package:hiddify/utils/utils.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:sliver_tools/sliver_tools.dart'; import 'package:sliver_tools/sliver_tools.dart';
@@ -76,20 +77,17 @@ class HomePage extends HookConsumerWidget {
], ],
), ),
), ),
if (MediaQuery.sizeOf(context).width < 840) if (MediaQuery.sizeOf(context).width < 840) const ActiveProxyFooter(),
const ActiveProxyFooter(),
], ],
), ),
), ),
], ],
), ),
AsyncData() => switch (hasAnyProfile) { AsyncData() => switch (hasAnyProfile) {
AsyncData(value: true) => AsyncData(value: true) => const EmptyActiveProfileHomeBody(),
const EmptyActiveProfileHomeBody(),
_ => const EmptyProfilesHomeBody(), _ => const EmptyProfilesHomeBody(),
}, },
AsyncError(:final error) => AsyncError(:final error) => SliverErrorBodyPlaceholder(t.presentShortError(error)),
SliverErrorBodyPlaceholder(t.presentShortError(error)),
_ => const SliverToBoxAdapter(), _ => const SliverToBoxAdapter(),
}, },
], ],

View File

@@ -1,11 +1,16 @@
import 'dart:io';
import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/localization/translations.dart';
import 'package:hiddify/core/widget/animated_visibility.dart'; import 'package:hiddify/core/widget/animated_visibility.dart';
import 'package:hiddify/core/widget/shimmer_skeleton.dart'; import 'package:hiddify/core/widget/shimmer_skeleton.dart';
import 'package:hiddify/features/connection/model/connection_status.dart';
import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart';
import 'package:hiddify/features/system_tray/notifier/system_tray_notifier.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:tray_manager/tray_manager.dart';
class ActiveProxyDelayIndicator extends HookConsumerWidget { class ActiveProxyDelayIndicator extends HookConsumerWidget {
const ActiveProxyDelayIndicator({super.key}); const ActiveProxyDelayIndicator({super.key});
@@ -24,17 +29,15 @@ class ActiveProxyDelayIndicator extends HookConsumerWidget {
case AsyncData(value: final proxy): case AsyncData(value: final proxy):
final delay = proxy.urlTestDelay; final delay = proxy.urlTestDelay;
final timeout = delay > 65000; final timeout = delay > 65000;
return Center( return Center(
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
await ref await ref.read(activeProxyNotifierProvider.notifier).urlTest(proxy.tag);
.read(activeProxyNotifierProvider.notifier)
.urlTest(proxy.tag);
}, },
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
child: Padding( child: Padding(
padding: padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -42,9 +45,7 @@ class ActiveProxyDelayIndicator extends HookConsumerWidget {
const Gap(8), const Gap(8),
if (delay > 0) if (delay > 0)
Text.rich( Text.rich(
semanticsLabel: timeout semanticsLabel: timeout ? t.proxies.delaySemantics.timeout : t.proxies.delaySemantics.result(delay: delay),
? t.proxies.delaySemantics.timeout
: t.proxies.delaySemantics.result(delay: delay),
TextSpan( TextSpan(
children: [ children: [
if (timeout) if (timeout)
@@ -58,8 +59,7 @@ class ActiveProxyDelayIndicator extends HookConsumerWidget {
else ...[ else ...[
TextSpan( TextSpan(
text: delay.toString(), text: delay.toString(),
style: theme.textTheme.titleMedium style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
?.copyWith(fontWeight: FontWeight.bold),
), ),
const TextSpan(text: " ms"), const TextSpan(text: " ms"),
], ],

View File

@@ -78,11 +78,11 @@ class IpInfoNotifier extends _$IpInfoNotifier with AppLogger {
} }
} }
@riverpod @Riverpod(keepAlive: true)
class ActiveProxyNotifier extends _$ActiveProxyNotifier with AppLogger { class ActiveProxyNotifier extends _$ActiveProxyNotifier with AppLogger {
@override @override
Stream<ProxyItemEntity> build() async* { Stream<ProxyItemEntity> build() async* {
ref.disposeDelay(const Duration(seconds: 20)); // ref.disposeDelay(const Duration(seconds: 20));
final serviceRunning = await ref.watch(serviceRunningProvider.future); final serviceRunning = await ref.watch(serviceRunningProvider.future);
if (!serviceRunning) { if (!serviceRunning) {