mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-28 23:08:59 +02:00
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.)
This commit is contained in:
@@ -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<MetadataProviderPriorityPage> createState() =>
|
||||
_MetadataProviderPriorityPageState();
|
||||
}
|
||||
|
||||
class _MetadataProviderPriorityPageState
|
||||
extends ConsumerState<MetadataProviderPriorityPage> {
|
||||
late List<String> _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<void> _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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> Function(WidgetRef ref) loadAllProviders;
|
||||
final List<String> Function(WidgetRef ref) currentPriority;
|
||||
final Future<void> Function(WidgetRef ref, List<String> 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<ProviderPriorityListPage> createState() =>
|
||||
_ProviderPriorityListPageState();
|
||||
}
|
||||
|
||||
class _ProviderPriorityListPageState
|
||||
extends ConsumerState<ProviderPriorityListPage> {
|
||||
late List<String> _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<void> _saveChanges() async {
|
||||
await widget.save(ref, _providers);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_hasChanges = false;
|
||||
});
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(widget.savedSnackbar(context))));
|
||||
}
|
||||
}
|
||||
@@ -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<ProviderPriorityPage> createState() =>
|
||||
_ProviderPriorityPageState();
|
||||
}
|
||||
|
||||
class _ProviderPriorityPageState extends ConsumerState<ProviderPriorityPage> {
|
||||
late List<String> _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<void> _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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user