diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 04d6eaad..04967646 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -143,6 +143,7 @@ "connectionConnected": "Connected", "connectionDisconnected": "Disconnected", "connectionReconnecting": "Reconnecting", + "connectionServerDown": "Unable to Connect", "accountConnections": "Account Connections", "accountConnectionsDescription": "Manage your external account connections", "accountConnectionAdd": "Add Connection", diff --git a/lib/widgets/app_wrapper.dart b/lib/widgets/app_wrapper.dart index d787c7eb..5a6fe784 100644 --- a/lib/widgets/app_wrapper.dart +++ b/lib/widgets/app_wrapper.dart @@ -33,7 +33,6 @@ class AppWrapper extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final networkStateShowing = useState(false); - final wsNotifier = ref.watch(websocketStateProvider.notifier); final websocketState = ref.watch(websocketStateProvider); final isShowSnow = useState(false); final isSnowGone = useState(false); @@ -47,8 +46,7 @@ class AppWrapper extends HookConsumerWidget { context: context, isScrollControlled: true, isDismissible: false, - builder: (context) => - NetworkStatusSheet(onReconnect: () => wsNotifier.connect()), + builder: (context) => NetworkStatusSheet(autoClose: true), ).then((_) => networkStateShowing.value = false); }); } diff --git a/lib/widgets/content/network_status_sheet.dart b/lib/widgets/content/network_status_sheet.dart index 388c11af..54f5ddde 100644 --- a/lib/widgets/content/network_status_sheet.dart +++ b/lib/widgets/content/network_status_sheet.dart @@ -1,78 +1,117 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/pods/websocket.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:island/widgets/content/sheet.dart'; +import 'package:styled_widget/styled_widget.dart'; class NetworkStatusSheet extends HookConsumerWidget { - final VoidCallback onReconnect; - - const NetworkStatusSheet({super.key, required this.onReconnect}); + final bool autoClose; + const NetworkStatusSheet({super.key, this.autoClose = false}); @override Widget build(BuildContext context, WidgetRef ref) { final ws = ref.watch(websocketProvider); final wsState = ref.watch(websocketStateProvider); + final wsNotifier = ref.watch(websocketStateProvider.notifier); + + useEffect(() { + if (!autoClose) return; + + final checks = [wsState == WebSocketState.connected()]; + if (!checks.any((e) => !e)) { + Future.delayed(Duration(seconds: 3), () { + if (context.mounted) Navigator.of(context).pop(); + }); + } + + return null; + }, [wsState]); + return SheetScaffold( - heightFactor: 0.4, - titleText: - wsState == WebSocketState.connected() - ? 'Connection Status' - : 'Connection Issue', + heightFactor: 0.6, + titleText: wsState == WebSocketState.connected() + ? 'Connection Status' + : 'Connection Issue', child: Padding( - padding: const EdgeInsets.all(20), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - wsState.when( - connected: - () => Text( - 'Connected to server', - style: Theme.of(context).textTheme.bodyLarge, - ), - connecting: - () => Text( - 'Connecting to server...', - style: Theme.of(context).textTheme.bodyLarge, - ), - disconnected: - () => Text( - 'Disconnected from server', - style: Theme.of(context).textTheme.bodyLarge, - ), - serverDown: - () => Text( - 'The server is not available right now... Please try again later...', - style: Theme.of(context).textTheme.bodyLarge, - ), - duplicateDevice: - () => Text( + Row( + spacing: 8, + children: [ + Text('WebSocket').bold(), + wsState.when( + connected: () => Text('connectionConnected').tr(), + connecting: () => Text('connectionReconnecting').tr(), + disconnected: () => Text('connectionDisconnected').tr(), + serverDown: () => Text('connectionServerDown').tr(), + duplicateDevice: () => Text( 'Another device has connected with the same account.', - style: Theme.of(context).textTheme.bodyLarge, ), - error: - (message) => Text( - 'Connection error: $message', - style: Theme.of(context).textTheme.bodyLarge, + error: (message) => Text('Connection error: $message'), + ), + if (ws.heartbeatDelay != null) + Text('${ws.heartbeatDelay!.inMilliseconds}ms'), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: wsState.when( + connected: () => Icon( + Symbols.check_circle, + key: ValueKey(WebSocketState.connected), + color: Colors.green, + size: 16, + ), + connecting: () => Icon( + Symbols.sync, + key: ValueKey(WebSocketState.connecting), + color: Colors.orange, + size: 16, + ), + disconnected: () => Icon( + Symbols.wifi_off, + key: ValueKey(WebSocketState.disconnected), + color: Colors.grey, + size: 16, + ), + serverDown: () => Icon( + Symbols.cloud_off, + key: ValueKey(WebSocketState.serverDown), + color: Colors.red, + size: 16, + ), + duplicateDevice: () => Icon( + Symbols.devices, + key: ValueKey(WebSocketState.duplicateDevice), + color: Colors.orange, + size: 16, + ), + error: (message) => Icon( + Symbols.error, + key: ValueKey(WebSocketState.error), + color: Colors.red, + size: 16, + ), ), + ), + ], ), - const SizedBox(height: 16), - if (ws.heartbeatDelay != null) - Text( - 'Last heartbeat: ${ws.heartbeatDelay!.inMilliseconds}ms', - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 24), - Center( - child: FilledButton.icon( - icon: const Icon(Symbols.wifi), - label: const Text('Reconnect'), - onPressed: () { - onReconnect(); - Navigator.pop(context); - }, - ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + spacing: 8, + children: [ + FilledButton.icon( + icon: const Icon(Symbols.wifi), + label: const Text('Reconnect'), + onPressed: () { + wsNotifier.manualReconnect(); + }, + ), + ], ), ], ), diff --git a/lib/widgets/debug_sheet.dart b/lib/widgets/debug_sheet.dart index 57da0923..1564d3ee 100644 --- a/lib/widgets/debug_sheet.dart +++ b/lib/widgets/debug_sheet.dart @@ -5,7 +5,6 @@ import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/pods/message.dart'; import 'package:island/pods/network.dart'; -import 'package:island/pods/websocket.dart'; import 'package:island/services/update_service.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/content/network_status_sheet.dart'; @@ -67,8 +66,6 @@ class DebugSheet extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final wsNotifier = ref.watch(websocketStateProvider.notifier); - return SheetScaffold( titleText: 'Debug', heightFactor: 0.6, @@ -111,10 +108,7 @@ class DebugSheet extends HookConsumerWidget { showModalBottomSheet( context: context, isScrollControlled: true, - builder: - (context) => NetworkStatusSheet( - onReconnect: () => wsNotifier.connect(), - ), + builder: (context) => NetworkStatusSheet(), ); }, ),