refactor(extensions): share priority settings links

This commit is contained in:
zarzet committed 2026-10-18 00:46:52 +07:00
1 parent 12119530a7
commit ad9b2b2ea5
2 files changed
+223 -191

No files matched your search

+74 -191
View File
@@ -434,55 +434,46 @@ String _extensionHealthLabel(BuildContext context, String status) {
}
}
class _DownloadPriorityItem extends ConsumerWidget {
const _DownloadPriorityItem();
class _ExtensionPriorityLink extends StatelessWidget {
const _ExtensionPriorityLink({
required this.icon,
required this.title,
required this.subtitle,
required this.enabled,
required this.onTap,
});
final IconData icon;
final String title;
final String subtitle;
final bool enabled;
final VoidCallback onTap;
@override
Widget build(BuildContext context, WidgetRef ref) {
final extState = ref.watch(extensionProvider);
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final hasDownloadExtensions = extState.extensions.any(
(e) => e.enabled && e.hasDownloadProvider,
);
if (context.isMornye) {
return Opacity(
opacity: hasDownloadExtensions ? 1 : 0.5,
opacity: enabled ? 1 : 0.5,
child: SettingsItem(
icon: Icons.download,
title: context.l10n.extensionsDownloadPriority,
subtitle: hasDownloadExtensions
? context.l10n.extensionsDownloadPrioritySubtitle
: context.l10n.extensionsNoDownloadProvider,
onTap: hasDownloadExtensions
? () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const ProviderPriorityPage(),
),
)
: null,
icon: icon,
title: title,
subtitle: subtitle,
onTap: enabled ? onTap : null,
),
);
}
final content = InkWell(
onTap: hasDownloadExtensions
? () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const ProviderPriorityPage(),
),
)
: null,
onTap: enabled ? onTap : null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
AppIcon(
Icons.download,
color: hasDownloadExtensions
icon,
color: enabled
? colorScheme.onSurfaceVariant
: colorScheme.outline,
),
@@ -492,16 +483,14 @@ class _DownloadPriorityItem extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.extensionsDownloadPriority,
title,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: hasDownloadExtensions ? null : colorScheme.outline,
color: enabled ? null : colorScheme.outline,
),
),
const SizedBox(height: 2),
Text(
hasDownloadExtensions
? context.l10n.extensionsDownloadPrioritySubtitle
: context.l10n.extensionsNoDownloadProvider,
subtitle,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -511,7 +500,7 @@ class _DownloadPriorityItem extends ConsumerWidget {
),
AppIcon(
Icons.chevron_right,
color: hasDownloadExtensions
color: enabled
? colorScheme.onSurfaceVariant
: colorScheme.outline,
),
@@ -519,9 +508,31 @@ class _DownloadPriorityItem extends ConsumerWidget {
),
),
);
return SettingsSearchTarget(
label: context.l10n.extensionsDownloadPriority,
child: content,
return SettingsSearchTarget(label: title, child: content);
}
}
class _DownloadPriorityItem extends ConsumerWidget {
const _DownloadPriorityItem();
@override
Widget build(BuildContext context, WidgetRef ref) {
final extState = ref.watch(extensionProvider);
final hasDownloadExtensions = extState.extensions.any(
(e) => e.enabled && e.hasDownloadProvider,
);
return _ExtensionPriorityLink(
icon: Icons.download,
title: context.l10n.extensionsDownloadPriority,
subtitle: hasDownloadExtensions
? context.l10n.extensionsDownloadPrioritySubtitle
: context.l10n.extensionsNoDownloadProvider,
enabled: hasDownloadExtensions,
onTap: () => Navigator.push(
context,
MaterialPageRoute<void>(builder: (_) => const ProviderPriorityPage()),
),
);
}
}
@@ -532,89 +543,25 @@ class _MetadataPriorityItem extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final extState = ref.watch(extensionProvider);
final colorScheme = Theme.of(context).colorScheme;
final hasMetadataExtensions = extState.extensions.any(
(e) => e.enabled && e.hasMetadataProvider,
);
if (context.isMornye) {
return Opacity(
opacity: hasMetadataExtensions ? 1 : 0.5,
child: SettingsItem(
icon: Icons.search,
title: context.l10n.extensionsMetadataPriority,
subtitle: hasMetadataExtensions
? context.l10n.extensionsMetadataPrioritySubtitle
: context.l10n.extensionsNoMetadataProvider,
onTap: hasMetadataExtensions
? () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const MetadataProviderPriorityPage(),
),
)
: null,
),
);
}
final content = InkWell(
onTap: hasMetadataExtensions
? () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const MetadataProviderPriorityPage(),
),
)
: null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
AppIcon(
Icons.search,
color: hasMetadataExtensions
? colorScheme.onSurfaceVariant
: colorScheme.outline,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.extensionsMetadataPriority,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: hasMetadataExtensions ? null : colorScheme.outline,
),
),
const SizedBox(height: 2),
Text(
hasMetadataExtensions
? context.l10n.extensionsMetadataPrioritySubtitle
: context.l10n.extensionsNoMetadataProvider,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
AppIcon(
Icons.chevron_right,
color: hasMetadataExtensions
? colorScheme.onSurfaceVariant
: colorScheme.outline,
),
],
return _ExtensionPriorityLink(
icon: Icons.search,
title: context.l10n.extensionsMetadataPriority,
subtitle: hasMetadataExtensions
? context.l10n.extensionsMetadataPrioritySubtitle
: context.l10n.extensionsNoMetadataProvider,
enabled: hasMetadataExtensions,
onTap: () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const MetadataProviderPriorityPage(),
),
),
);
return SettingsSearchTarget(
label: context.l10n.extensionsMetadataPriority,
child: content,
);
}
}
@@ -624,89 +571,25 @@ class _DownloadFallbackItem extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final extState = ref.watch(extensionProvider);
final colorScheme = Theme.of(context).colorScheme;
final hasDownloadExtensions = extState.extensions.any(
(e) => e.enabled && e.hasDownloadProvider,
);
if (context.isMornye) {
return Opacity(
opacity: hasDownloadExtensions ? 1 : 0.5,
child: SettingsItem(
icon: Icons.alt_route,
title: context.l10n.extensionsFallbackTitle,
subtitle: hasDownloadExtensions
? context.l10n.extensionsFallbackSubtitle
: context.l10n.extensionsNoDownloadProvider,
onTap: hasDownloadExtensions
? () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const DownloadFallbackExtensionsPage(),
),
)
: null,
),
);
}
final content = InkWell(
onTap: hasDownloadExtensions
? () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const DownloadFallbackExtensionsPage(),
),
)
: null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
AppIcon(
Icons.alt_route,
color: hasDownloadExtensions
? colorScheme.onSurfaceVariant
: colorScheme.outline,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.extensionsFallbackTitle,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: hasDownloadExtensions ? null : colorScheme.outline,
),
),
const SizedBox(height: 2),
Text(
hasDownloadExtensions
? context.l10n.extensionsFallbackSubtitle
: context.l10n.extensionsNoDownloadProvider,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
AppIcon(
Icons.chevron_right,
color: hasDownloadExtensions
? colorScheme.onSurfaceVariant
: colorScheme.outline,
),
],
return _ExtensionPriorityLink(
icon: Icons.alt_route,
title: context.l10n.extensionsFallbackTitle,
subtitle: hasDownloadExtensions
? context.l10n.extensionsFallbackSubtitle
: context.l10n.extensionsNoDownloadProvider,
enabled: hasDownloadExtensions,
onTap: () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const DownloadFallbackExtensionsPage(),
),
),
);
return SettingsSearchTarget(
label: context.l10n.extensionsFallbackTitle,
child: content,
);
}
}
+149
View File
@@ -0,0 +1,149 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:spotiflac_android/l10n/app_localizations.dart';
import 'package:spotiflac_android/models/settings.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/screens/settings/download_fallback_extensions_page.dart';
import 'package:spotiflac_android/screens/settings/extensions_page.dart';
import 'package:spotiflac_android/screens/settings/metadata_provider_priority_page.dart';
import 'package:spotiflac_android/screens/settings/provider_priority_page.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:spotiflac_android/widgets/settings_group.dart';
class _Settings extends SettingsNotifier {
@override
AppSettings build() => const AppSettings();
}
class _Extensions extends ExtensionNotifier {
_Extensions(this.extension);
final Extension extension;
@override
ExtensionState build() =>
ExtensionState(isInitialized: true, extensions: [extension]);
}
void main() {
const extension = Extension(
id: 'example.music',
name: 'example.music',
displayName: 'Example Music',
version: '1.0.0',
description: 'Example provider',
enabled: true,
status: 'loaded',
hasDownloadProvider: true,
hasMetadataProvider: true,
);
final rows = [
('Download Priority', 'Set download service order', ProviderPriorityPage),
(
'Metadata Priority',
'Set search & metadata source order',
MetadataProviderPriorityPage,
),
(
'Fallback Extensions',
'Choose which installed download extensions can be used as fallback',
DownloadFallbackExtensionsPage,
),
];
for (final mornye in [false, true]) {
for (final (enabled, download, metadata) in [
(true, true, true),
(false, true, true),
(true, false, true),
(true, true, false),
]) {
for (final (title, subtitle, destination) in rows) {
testWidgets('$title ($mornye, $enabled, $download, $metadata)', (
tester,
) async {
final available =
enabled &&
(destination == MetadataProviderPriorityPage
? metadata
: download);
await tester.pumpWidget(
ProviderScope(
overrides: [
settingsProvider.overrideWith(_Settings.new),
extensionProvider.overrideWith(
() => _Extensions(
extension.copyWith(
enabled: enabled,
hasDownloadProvider: download,
hasMetadataProvider: metadata,
),
),
),
],
child: MaterialApp(
theme: mornye
? MornyeTheme.build(Brightness.dark)
: ThemeData(),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: SettingsSearchHighlightScope(
targetLabel: title,
child: const ExtensionsPage(),
),
),
),
);
await tester.pumpAndSettle();
final target = find.byWidgetPredicate(
(widget) => widget is SettingsSearchTarget && widget.label == title,
);
expect(target, findsOneWidget);
expect(
find.byKey(ValueKey('settings-highlight:$title')),
findsOneWidget,
);
expect(find.text(title).hitTestable(), findsOneWidget);
expect(
find.descendant(
of: target,
matching: find.text(
available
? subtitle
: 'No extensions with ${destination == MetadataProviderPriorityPage ? 'metadata' : 'download'} provider',
),
),
findsOneWidget,
);
final settingsItem = find.ancestor(
of: target,
matching: find.byType(SettingsItem),
);
expect(settingsItem, mornye ? findsOneWidget : findsNothing);
if (mornye) {
final opacity = find.ancestor(
of: settingsItem,
matching: find.byType(Opacity),
);
expect(
tester.widget<Opacity>(opacity.first).opacity,
available ? 1 : 0.5,
);
}
await tester.tap(find.text(title));
await tester.pumpAndSettle();
expect(
find.byType(destination),
available ? findsOneWidget : findsNothing,
);
expect(
find.byType(ExtensionsPage),
available ? findsNothing : findsOneWidget,
);
expect(tester.takeException(), isNull);
await tester.pumpWidget(const SizedBox.shrink());
});
}
}
}
}