diff --git a/lib/core/widgets/server_endpoint_dialog.dart b/lib/core/widgets/server_endpoint_dialog.dart deleted file mode 100644 index 66d6e9d..0000000 --- a/lib/core/widgets/server_endpoint_dialog.dart +++ /dev/null @@ -1,128 +0,0 @@ -// Author: fruit.com.cn - -import 'package:flutter/material.dart'; - -import '../../l10n/app_localizations.dart'; -import '../config/app_config.dart'; -import '../config/server_endpoint.dart'; -import '../theme/app_colors.dart'; - -/// Opens the debug-only runtime server address editor. -Future showServerEndpointDialog( - BuildContext context, - AppLocalizations l10n, -) async { - final changed = await showDialog( - context: context, - builder: (_) => _ServerEndpointDialog(l10n: l10n), - ); - if (changed == true && context.mounted) { - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text(l10n.restartToApply))); - return true; - } - return false; -} - -class _ServerEndpointDialog extends StatefulWidget { - const _ServerEndpointDialog({required this.l10n}); - - final AppLocalizations l10n; - - @override - State<_ServerEndpointDialog> createState() => _ServerEndpointDialogState(); -} - -class _ServerEndpointDialogState extends State<_ServerEndpointDialog> { - late final TextEditingController _controller = TextEditingController( - text: AppConfig.apiBaseUrl, - ); - String? _error; - bool _saving = false; - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - Future _save() async { - final normalized = ServerEndpoint.normalize(_controller.text); - if (normalized == null) { - setState(() => _error = widget.l10n.invalidServerAddress); - return; - } - setState(() { - _saving = true; - _error = null; - }); - final saved = await ServerEndpoint.save(normalized); - if (!mounted) return; - if (!saved) { - setState(() { - _saving = false; - _error = widget.l10n.invalidServerAddress; - }); - return; - } - Navigator.pop(context, true); - } - - Future _restoreDefault() async { - setState(() => _saving = true); - await ServerEndpoint.clear(); - if (mounted) Navigator.pop(context, true); - } - - @override - Widget build(BuildContext context) { - final l10n = widget.l10n; - return AlertDialog( - title: Text(l10n.serverAddress), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextField( - key: const Key('server-endpoint-input'), - controller: _controller, - enabled: !_saving, - keyboardType: TextInputType.url, - autocorrect: false, - decoration: InputDecoration( - hintText: l10n.serverAddressHint, - errorText: _error, - errorMaxLines: 2, - ), - ), - const SizedBox(height: 10), - Text( - l10n.restartToApply, - key: const Key('server-endpoint-restart-hint'), - style: const TextStyle(color: AppColors.muted, fontSize: 12), - ), - const SizedBox(height: 4), - TextButton( - key: const Key('server-endpoint-restore'), - onPressed: _saving ? null : _restoreDefault, - child: Text(l10n.restoreDefault), - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: _saving ? null : () => Navigator.pop(context, false), - child: Text(l10n.cancel), - ), - FilledButton( - key: const Key('server-endpoint-save'), - onPressed: _saving ? null : _save, - child: Text(l10n.saveAddress), - ), - ], - ); - } -} diff --git a/lib/pages/home/home_view.dart b/lib/pages/home/home_view.dart index 9dddeb5..92c96b5 100644 --- a/lib/pages/home/home_view.dart +++ b/lib/pages/home/home_view.dart @@ -2,11 +2,9 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../../core/config/app_config.dart'; import '../../core/data/models/drama.dart'; import '../../core/data/models/home_feed.dart'; import '../../core/data/services/auth_controller.dart'; @@ -19,7 +17,6 @@ import '../../core/theme/app_theme.dart'; import '../../core/widgets/app_bottom_nav.dart'; import '../../core/widgets/auth_gate.dart'; import '../../core/widgets/empty_state.dart'; -import '../../core/widgets/server_endpoint_dialog.dart'; import '../../l10n/app_localizations.dart'; import '../discover/discover_view.dart'; import '../splash/splash_page.dart'; @@ -58,10 +55,7 @@ class HomePage extends StatelessWidget { if (catalog.error != null) { return Scaffold( backgroundColor: AppColors.ink, - body: HomeFailureView( - onRetry: catalog.load, - showDebugTools: kDebugMode, - ), + body: HomeFailureView(onRetry: catalog.load), ); } if (catalog.dramas.isEmpty) { @@ -86,53 +80,20 @@ class HomePage extends StatelessWidget { } } -/// Home catalog failure with optional debug-only endpoint controls. +/// Home catalog failure with a single recovery action. class HomeFailureView extends StatelessWidget { - const HomeFailureView({ - super.key, - required this.onRetry, - this.showDebugTools = kDebugMode, - }); + const HomeFailureView({super.key, required this.onRetry}); final VoidCallback onRetry; - final bool showDebugTools; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; - return Column( - children: [ - Expanded( - child: EmptyState( - icon: Icons.cloud_off_rounded, - title: l10n.catalogError, - actionLabel: l10n.retry, - onAction: onRetry, - ), - ), - if (showDebugTools) _debugTools(context, l10n), - ], - ); - } - - Widget _debugTools(BuildContext context, AppLocalizations l10n) { - return Padding( - padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), - child: Column( - children: [ - Text( - AppConfig.apiBaseUrl, - style: const TextStyle(fontSize: 12, color: AppColors.muted), - ), - const SizedBox(height: 8), - OutlinedButton.icon( - key: const Key('home-server-settings'), - onPressed: () => showServerEndpointDialog(context, l10n), - icon: const Icon(Icons.dns_outlined), - label: Text(l10n.serverSettings), - ), - ], - ), + return EmptyState( + icon: Icons.cloud_off_rounded, + title: l10n.catalogError, + actionLabel: l10n.retry, + onAction: onRetry, ); } } diff --git a/lib/pages/mine/mine_logic.dart b/lib/pages/mine/mine_logic.dart index 3c56aaf..f6e17fd 100644 --- a/lib/pages/mine/mine_logic.dart +++ b/lib/pages/mine/mine_logic.dart @@ -1,16 +1,13 @@ // Author: fruit.com.cn -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../../core/config/app_config.dart'; import '../../core/config/locale_controller.dart'; import '../../core/data/models/drama.dart'; import '../../core/data/models/watch_progress.dart'; import '../../core/data/repositories/watch_history_repository.dart'; import '../../core/theme/app_colors.dart'; -import '../../core/widgets/server_endpoint_dialog.dart'; import '../../l10n/app_localizations.dart'; enum MyPageAction { home, discover, resume } @@ -114,10 +111,7 @@ class MineLogic extends ChangeNotifier { } } - Future showLanguage( - BuildContext context, { - bool showServerSetting = kDebugMode, - }) async { + Future showLanguage(BuildContext context) async { final l10n = AppLocalizations.of(context)!; await showModalBottomSheet( context: context, @@ -146,17 +140,6 @@ class MineLogic extends ChangeNotifier { Navigator.pop(sheetContext); }, ), - if (showServerSetting) ...[ - const Divider(height: 1), - ListTile( - key: const Key('server-endpoint-setting'), - leading: const Icon(Icons.dns_outlined), - title: Text(l10n.serverAddress), - subtitle: Text(AppConfig.apiBaseUrl), - trailing: const Icon(Icons.edit_outlined), - onTap: () => _editServerEndpoint(context, l10n), - ), - ], const SizedBox(height: 8), ], ), @@ -166,11 +149,6 @@ class MineLogic extends ChangeNotifier { ); } - Future _editServerEndpoint( - BuildContext context, - AppLocalizations l10n, - ) => showServerEndpointDialog(context, l10n); - @override void dispose() { _disposed = true; diff --git a/lib/pages/mine/mine_view.dart b/lib/pages/mine/mine_view.dart index 174397f..4174e76 100644 --- a/lib/pages/mine/mine_view.dart +++ b/lib/pages/mine/mine_view.dart @@ -2,7 +2,6 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -49,7 +48,6 @@ class MyPage extends StatefulWidget { this.dramas, this.onDramaSelected, this.wallet, - this.debugShowSetting = kDebugMode, }); final Drama drama; @@ -67,7 +65,6 @@ class MyPage extends StatefulWidget { final List? dramas; final ValueChanged? onDramaSelected; final WalletController? wallet; - final bool debugShowSetting; @override State createState() => _MyPageState(); @@ -759,10 +756,7 @@ class _MyPageState extends State { }; return InkWell( key: const Key('mine-language'), - onTap: () => logic.showLanguage( - context, - showServerSetting: widget.debugShowSetting, - ), + onTap: () => logic.showLanguage(context), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.all(12), @@ -844,10 +838,7 @@ class _MyPageState extends State { _menuTile( icon: Icons.settings_rounded, title: l10n.settings, - onTap: () => logic.showLanguage( - context, - showServerSetting: widget.debugShowSetting, - ), + onTap: () => logic.showLanguage(context), ), ], ), diff --git a/test/home/home_server_setting_test.dart b/test/home/home_server_setting_test.dart index 239da4f..3278287 100644 --- a/test/home/home_server_setting_test.dart +++ b/test/home/home_server_setting_test.dart @@ -12,45 +12,23 @@ void main() { InMemorySharedPreferencesAsync.empty(); }); - testWidgets( - 'should_show_server_settings_and_open_dialog_when_debug_tools_are_enabled', - (tester) async { - await _pumpFailureView(tester, showDebugTools: true); - - final button = find.byKey(const Key('home-server-settings')); - expect(button, findsOneWidget); - expect(find.text('Server settings'), findsOneWidget); - - await tester.tap(button); - await tester.pumpAndSettle(); - - expect(find.byKey(const Key('server-endpoint-input')), findsOneWidget); - expect(find.text('Restart the app to apply.'), findsOneWidget); - }, - ); - - testWidgets('should_hide_server_settings_when_debug_tools_are_disabled', ( + testWidgets('should_not_expose_server_settings_on_catalog_failure', ( tester, ) async { - await _pumpFailureView(tester, showDebugTools: false); - + await _pumpFailureView(tester); expect(find.byKey(const Key('home-server-settings')), findsNothing); expect(find.text('Server settings'), findsNothing); + expect(find.text('Retry'), findsOneWidget); }); } -Future _pumpFailureView( - WidgetTester tester, { - required bool showDebugTools, -}) async { +Future _pumpFailureView(WidgetTester tester) async { await tester.pumpWidget( MaterialApp( locale: const Locale('en'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, - home: Scaffold( - body: HomeFailureView(onRetry: () {}, showDebugTools: showDebugTools), - ), + home: Scaffold(body: HomeFailureView(onRetry: () {})), ), ); await tester.pumpAndSettle(); diff --git a/test/mine/server_endpoint_setting_test.dart b/test/mine/server_endpoint_setting_test.dart index 7eb9c49..2d468fb 100644 --- a/test/mine/server_endpoint_setting_test.dart +++ b/test/mine/server_endpoint_setting_test.dart @@ -1,12 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:fruitshorts/core/config/locale_controller.dart'; -import 'package:fruitshorts/core/config/server_endpoint.dart'; import 'package:fruitshorts/core/data/models/drama.dart'; import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart'; import 'package:fruitshorts/l10n/app_localizations.dart'; @@ -25,49 +23,26 @@ void main() { InMemorySharedPreferencesAsync.empty(); }); - testWidgets('should_show_server_address_when_debug_entry_is_enabled', ( + testWidgets('should_not_show_server_address_in_settings_sheet', ( tester, ) async { - await _pumpSettings(tester, showServerSetting: true); - expect(find.byKey(const Key('server-endpoint-setting')), findsOneWidget); - expect(find.text('Server address'), findsOneWidget); - }); - - testWidgets('should_hide_server_address_when_debug_entry_is_disabled', ( - tester, - ) async { - await _pumpSettings(tester, showServerSetting: false); + await _pumpSettings(tester); expect(find.byKey(const Key('server-endpoint-setting')), findsNothing); expect(find.text('Server address'), findsNothing); }); - testWidgets('should_reject_invalid_server_address_without_saving', ( + testWidgets('should_keep_all_three_languages_in_settings_sheet', ( tester, ) async { - await _pumpSettings(tester, showServerSetting: true); - await tester.tap(find.byKey(const Key('server-endpoint-setting'))); - await tester.pumpAndSettle(); + await _pumpSettings(tester); - expect(find.text('Restart the app to apply.'), findsOneWidget); - await tester.enterText( - find.byKey(const Key('server-endpoint-input')), - 'ftp://invalid', - ); - await tester.tap(find.byKey(const Key('server-endpoint-save'))); - await tester.pumpAndSettle(); - - expect(find.text('Enter a valid http(s) server address.'), findsOneWidget); - expect( - await SharedPreferencesAsync().getString(ServerEndpoint.preferenceKey), - isNull, - ); + expect(find.text('English'), findsOneWidget); + expect(find.text('简体中文'), findsOneWidget); + expect(find.text('Bahasa Indonesia'), findsOneWidget); }); } -Future _pumpSettings( - WidgetTester tester, { - required bool showServerSetting, -}) async { +Future _pumpSettings(WidgetTester tester) async { final logic = MineLogic( history: InMemoryWatchHistoryRepository(), drama: _drama, @@ -85,10 +60,7 @@ Future _pumpSettings( builder: (context) => Scaffold( body: TextButton( key: const Key('open-settings'), - onPressed: () => logic.showLanguage( - context, - showServerSetting: showServerSetting, - ), + onPressed: () => logic.showLanguage(context), child: const Text('Open settings'), ), ),