From ca1c95346fd355067c6a1bda09805e46ea6cff2a Mon Sep 17 00:00:00 2001 From: zarzet Date: Tue, 14 Jul 2026 18:55:48 +0700 Subject: [PATCH] refactor(settings): share the provider-priority reorder page The download- and metadata-provider priority pages were 99% identical. Extract ProviderPriorityListPage taking the provider-list load/save callbacks and labels; both pages become thin wrappers. (The metadata page's explicit saveLabel was the same dialogSave the scaffold already defaults to.) --- .../metadata_provider_priority_page.dart | 111 ++-------------- .../settings/provider_priority_list_page.dart | 123 ++++++++++++++++++ .../settings/provider_priority_page.dart | 105 ++------------- 3 files changed, 149 insertions(+), 190 deletions(-) create mode 100644 lib/screens/settings/provider_priority_list_page.dart diff --git a/lib/screens/settings/metadata_provider_priority_page.dart b/lib/screens/settings/metadata_provider_priority_page.dart index 386bb1ca..4e8fc735 100644 --- a/lib/screens/settings/metadata_provider_priority_page.dart +++ b/lib/screens/settings/metadata_provider_priority_page.dart @@ -1,108 +1,25 @@ import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; -import 'package:spotiflac_android/utils/adaptive_layout.dart'; -import 'package:spotiflac_android/widgets/discard_changes_dialog.dart'; -import 'package:spotiflac_android/widgets/priority_settings_scaffold.dart'; -import 'package:spotiflac_android/widgets/reorderable_priority_item.dart'; +import 'package:spotiflac_android/screens/settings/provider_priority_list_page.dart'; -class MetadataProviderPriorityPage extends ConsumerStatefulWidget { +class MetadataProviderPriorityPage extends StatelessWidget { const MetadataProviderPriorityPage({super.key}); - @override - ConsumerState createState() => - _MetadataProviderPriorityPageState(); -} - -class _MetadataProviderPriorityPageState - extends ConsumerState { - late List _providers; - bool _hasChanges = false; - - @override - void initState() { - super.initState(); - _loadProviders(); - } - - void _loadProviders() { - final extState = ref.read(extensionProvider); - final allProviders = ref - .read(extensionProvider.notifier) - .getAllMetadataProviders(); - - if (extState.metadataProviderPriority.isNotEmpty) { - _providers = List.from(extState.metadataProviderPriority); - for (final provider in allProviders) { - if (!_providers.contains(provider)) { - _providers.add(provider); - } - } - _providers.removeWhere((p) => !allProviders.contains(p)); - } else { - _providers = allProviders; - } - } - @override Widget build(BuildContext context) { - return PrioritySettingsScaffold( - hasChanges: _hasChanges, - title: context.l10n.metadataProviderPriorityTitle, - description: context.l10n.metadataProviderPriorityDescription, - descriptionPadding: const EdgeInsets.all(16), - infoText: context.l10n.metadataProviderPriorityInfo, - saveLabel: context.l10n.dialogSave, - onSave: _saveChanges, - onConfirmDiscard: showDiscardChangesDialog, - slivers: [ - SliverPadding( - padding: EdgeInsets.symmetric( - horizontal: 16 + wideListInset(context), - ), - sliver: SliverReorderableList( - itemCount: _providers.length, - itemBuilder: (context, index) { - final provider = _providers[index]; - final extension = ref - .read(extensionProvider) - .extensions - .where((ext) => ext.id == provider) - .firstOrNull; - return ReorderablePriorityItem( - key: ValueKey(provider), - index: index, - isFirst: index == 0, - icon: Icons.extension, - iconColor: Theme.of(context).colorScheme.secondary, - name: extension?.displayName ?? provider, - subtitle: context.l10n.providerExtension, - ); - }, - onReorderItem: (oldIndex, newIndex) { - setState(() { - final item = _providers.removeAt(oldIndex); - _providers.insert(newIndex, item); - _hasChanges = true; - }); - }, - ), - ), - ], - ); - } - - Future _saveChanges() async { - await ref - .read(extensionProvider.notifier) - .setMetadataProviderPriority(_providers); - if (!mounted) return; - setState(() { - _hasChanges = false; - }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.snackbarMetadataProviderSaved)), + return ProviderPriorityListPage( + loadAllProviders: (ref) => + ref.read(extensionProvider.notifier).getAllMetadataProviders(), + currentPriority: (ref) => + ref.read(extensionProvider).metadataProviderPriority, + save: (ref, priority) => ref + .read(extensionProvider.notifier) + .setMetadataProviderPriority(priority), + title: (c) => c.l10n.metadataProviderPriorityTitle, + description: (c) => c.l10n.metadataProviderPriorityDescription, + infoText: (c) => c.l10n.metadataProviderPriorityInfo, + savedSnackbar: (c) => c.l10n.snackbarMetadataProviderSaved, ); } } diff --git a/lib/screens/settings/provider_priority_list_page.dart b/lib/screens/settings/provider_priority_list_page.dart new file mode 100644 index 00000000..dd47a442 --- /dev/null +++ b/lib/screens/settings/provider_priority_list_page.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotiflac_android/l10n/l10n.dart'; +import 'package:spotiflac_android/providers/extension_provider.dart'; +import 'package:spotiflac_android/utils/adaptive_layout.dart'; +import 'package:spotiflac_android/widgets/discard_changes_dialog.dart'; +import 'package:spotiflac_android/widgets/priority_settings_scaffold.dart'; +import 'package:spotiflac_android/widgets/reorderable_priority_item.dart'; + +/// Reorderable priority list shared by the download- and metadata-provider +/// priority pages; the two differ only in which provider list they read/write +/// and their labels, passed in as callbacks. +class ProviderPriorityListPage extends ConsumerStatefulWidget { + final List Function(WidgetRef ref) loadAllProviders; + final List Function(WidgetRef ref) currentPriority; + final Future Function(WidgetRef ref, List priority) save; + final String Function(BuildContext context) title; + final String Function(BuildContext context) description; + final String Function(BuildContext context) infoText; + final String Function(BuildContext context) savedSnackbar; + + const ProviderPriorityListPage({ + super.key, + required this.loadAllProviders, + required this.currentPriority, + required this.save, + required this.title, + required this.description, + required this.infoText, + required this.savedSnackbar, + }); + + @override + ConsumerState createState() => + _ProviderPriorityListPageState(); +} + +class _ProviderPriorityListPageState + extends ConsumerState { + late List _providers; + bool _hasChanges = false; + + @override + void initState() { + super.initState(); + _loadProviders(); + } + + void _loadProviders() { + final allProviders = widget.loadAllProviders(ref); + final saved = widget.currentPriority(ref); + + if (saved.isNotEmpty) { + _providers = List.from(saved); + for (final provider in allProviders) { + if (!_providers.contains(provider)) { + _providers.add(provider); + } + } + _providers.removeWhere((p) => !allProviders.contains(p)); + } else { + _providers = allProviders; + } + } + + @override + Widget build(BuildContext context) { + return PrioritySettingsScaffold( + hasChanges: _hasChanges, + title: widget.title(context), + description: widget.description(context), + descriptionPadding: const EdgeInsets.all(16), + infoText: widget.infoText(context), + onSave: _saveChanges, + onConfirmDiscard: showDiscardChangesDialog, + slivers: [ + SliverPadding( + padding: EdgeInsets.symmetric( + horizontal: 16 + wideListInset(context), + ), + sliver: SliverReorderableList( + itemCount: _providers.length, + itemBuilder: (context, index) { + final provider = _providers[index]; + final extension = ref + .read(extensionProvider) + .extensions + .where((ext) => ext.id == provider) + .firstOrNull; + return ReorderablePriorityItem( + key: ValueKey(provider), + index: index, + isFirst: index == 0, + icon: Icons.extension, + iconColor: Theme.of(context).colorScheme.secondary, + name: extension?.displayName ?? provider, + subtitle: context.l10n.providerExtension, + ); + }, + onReorderItem: (oldIndex, newIndex) { + setState(() { + final item = _providers.removeAt(oldIndex); + _providers.insert(newIndex, item); + _hasChanges = true; + }); + }, + ), + ), + ], + ); + } + + Future _saveChanges() async { + await widget.save(ref, _providers); + if (!mounted) return; + setState(() { + _hasChanges = false; + }); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(widget.savedSnackbar(context)))); + } +} diff --git a/lib/screens/settings/provider_priority_page.dart b/lib/screens/settings/provider_priority_page.dart index 9617ed0e..8d2a5ecb 100644 --- a/lib/screens/settings/provider_priority_page.dart +++ b/lib/screens/settings/provider_priority_page.dart @@ -1,104 +1,23 @@ import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; -import 'package:spotiflac_android/utils/adaptive_layout.dart'; -import 'package:spotiflac_android/widgets/discard_changes_dialog.dart'; -import 'package:spotiflac_android/widgets/priority_settings_scaffold.dart'; -import 'package:spotiflac_android/widgets/reorderable_priority_item.dart'; +import 'package:spotiflac_android/screens/settings/provider_priority_list_page.dart'; -class ProviderPriorityPage extends ConsumerStatefulWidget { +class ProviderPriorityPage extends StatelessWidget { const ProviderPriorityPage({super.key}); - @override - ConsumerState createState() => - _ProviderPriorityPageState(); -} - -class _ProviderPriorityPageState extends ConsumerState { - late List _providers; - bool _hasChanges = false; - - @override - void initState() { - super.initState(); - _loadProviders(); - } - - void _loadProviders() { - final extState = ref.read(extensionProvider); - final allProviders = ref - .read(extensionProvider.notifier) - .getAllDownloadProviders(); - - if (extState.providerPriority.isNotEmpty) { - _providers = List.from(extState.providerPriority); - for (final provider in allProviders) { - if (!_providers.contains(provider)) { - _providers.add(provider); - } - } - _providers.removeWhere((p) => !allProviders.contains(p)); - } else { - _providers = allProviders; - } - } - @override Widget build(BuildContext context) { - return PrioritySettingsScaffold( - hasChanges: _hasChanges, - title: context.l10n.providerPriorityTitle, - description: context.l10n.providerPriorityDescription, - descriptionPadding: const EdgeInsets.all(16), - infoText: context.l10n.providerPriorityInfo, - onSave: _saveChanges, - onConfirmDiscard: showDiscardChangesDialog, - slivers: [ - SliverPadding( - padding: EdgeInsets.symmetric( - horizontal: 16 + wideListInset(context), - ), - sliver: SliverReorderableList( - itemCount: _providers.length, - itemBuilder: (context, index) { - final provider = _providers[index]; - final extension = ref - .read(extensionProvider) - .extensions - .where((ext) => ext.id == provider) - .firstOrNull; - return ReorderablePriorityItem( - key: ValueKey(provider), - index: index, - isFirst: index == 0, - icon: Icons.extension, - iconColor: Theme.of(context).colorScheme.secondary, - name: extension?.displayName ?? provider, - subtitle: context.l10n.providerExtension, - ); - }, - onReorderItem: (oldIndex, newIndex) { - setState(() { - final item = _providers.removeAt(oldIndex); - _providers.insert(newIndex, item); - _hasChanges = true; - }); - }, - ), - ), - ], - ); - } - - Future _saveChanges() async { - await ref.read(extensionProvider.notifier).setProviderPriority(_providers); - if (!mounted) return; - setState(() { - _hasChanges = false; - }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.snackbarProviderPrioritySaved)), + return ProviderPriorityListPage( + loadAllProviders: (ref) => + ref.read(extensionProvider.notifier).getAllDownloadProviders(), + currentPriority: (ref) => ref.read(extensionProvider).providerPriority, + save: (ref, priority) => + ref.read(extensionProvider.notifier).setProviderPriority(priority), + title: (c) => c.l10n.providerPriorityTitle, + description: (c) => c.l10n.providerPriorityDescription, + infoText: (c) => c.l10n.providerPriorityInfo, + savedSnackbar: (c) => c.l10n.snackbarProviderPrioritySaved, ); } }