refactor: share the paginated grid tab, cached remote store, and tile focus

- PaginatedCardGridTabState: the collections and playlists tabs were 95%
  identical; they now supply only pageSize/fetchPage/idOf instead of each
  duplicating the grid, memo, inflation budget and focus wiring.
- EtagCachedRemoteStore: the anime-lists and fribb mapping stores now share
  one download/cache/isolate-parse/conditional-GET lifecycle.
- FocusableTileStateMixin manages its own initState/didUpdateWidget/dispose
  instead of requiring every caller to forward three lifecycle hooks.

Also drops unused ServerCapabilities entries and dead code in
focusable_list_tile and music/track_row.
This commit is contained in:
edde746
2026-07-26 06:09:48 +02:00
parent 352b88109b
commit 316a69a1de
56 changed files with 615 additions and 1455 deletions
@@ -15,7 +15,6 @@ import 'package:plezy/providers/hidden_libraries_provider.dart';
import 'package:plezy/providers/libraries_provider.dart';
import 'package:plezy/providers/multi_server_provider.dart';
import 'package:plezy/theme/mono_theme.dart';
import 'package:plezy/services/data_aggregation_service.dart';
import 'package:plezy/services/multi_server_manager.dart';
import 'package:plezy/services/plex_api_cache.dart';
import 'package:plezy/utils/platform_detector.dart';
@@ -24,6 +23,7 @@ import 'package:plezy/widgets/overlay_sheet.dart';
import 'package:provider/provider.dart';
import '../test_helpers/backend_client_fixtures.dart';
import '../test_helpers/multi_server_fixtures.dart';
import '../test_helpers/prefs.dart';
@@ -49,8 +49,7 @@ Future<({int Function() selects, int Function() backs})> _pumpLibraryManagementL
addTearDown(hiddenLibrariesProvider.dispose);
final fallbackManager = multiServerProvider == null ? MultiServerManager() : null;
final effectiveMultiServerProvider =
multiServerProvider ?? MultiServerProvider(fallbackManager!, DataAggregationService(fallbackManager));
final effectiveMultiServerProvider = multiServerProvider ?? testMultiServerProvider(fallbackManager!);
if (fallbackManager != null) {
addTearDown(() {
effectiveMultiServerProvider.dispose();
@@ -268,7 +267,7 @@ class _LibraryActionHarness {
);
manager.debugRegisterClientForTesting(owner);
}
provider = MultiServerProvider(manager, DataAggregationService(manager));
provider = testMultiServerProvider(manager);
}
void dispose() {
+6 -6
View File
@@ -30,7 +30,6 @@ import 'package:plezy/providers/multi_server_provider.dart';
import 'package:plezy/providers/playback_state_provider.dart';
import 'package:plezy/screens/music/album_detail_screen.dart';
import 'package:plezy/screens/music/artist_detail_screen.dart';
import 'package:plezy/services/data_aggregation_service.dart';
import 'package:plezy/services/download_manager_service.dart';
import 'package:plezy/services/download_storage_service.dart';
import 'package:plezy/services/jellyfin_client.dart';
@@ -46,6 +45,7 @@ import 'package:plezy/widgets/media_context_menu.dart';
import 'package:provider/provider.dart';
import '../test_helpers/backend_client_fixtures.dart';
import '../test_helpers/media_items.dart';
import '../test_helpers/multi_server_fixtures.dart';
import '../test_helpers/prefs.dart';
import '../test_helpers/profile_stack.dart';
@@ -124,7 +124,7 @@ void main() {
final client = _AudioPlaylistClient(tracks);
final music = _RecordingMusicPlaybackService();
final manager = MultiServerManager()..debugRegisterClientForTesting(client);
final multiServerProvider = MultiServerProvider(manager, DataAggregationService(manager));
final multiServerProvider = testMultiServerProvider(manager);
final stack = await ProfileStack.create(withStorage: false);
addTearDown(() async {
await stack.dispose();
@@ -228,7 +228,7 @@ void main() {
])..blockWithAbort = true;
final playback = PlaybackStateProvider();
final manager = MultiServerManager()..debugRegisterClientForTesting(client);
final multiServerProvider = MultiServerProvider(manager, DataAggregationService(manager));
final multiServerProvider = testMultiServerProvider(manager);
final stack = await ProfileStack.create(withStorage: false);
addTearDown(() async {
playback.dispose();
@@ -293,7 +293,7 @@ void main() {
addTearDown(() => TvDetectionService.debugSetAppleTVOverride(null));
final manager = MultiServerManager();
final multiServerProvider = MultiServerProvider(manager, DataAggregationService(manager));
final multiServerProvider = testMultiServerProvider(manager);
final stack = await ProfileStack.create(withStorage: false);
addTearDown(() async {
await stack.dispose();
@@ -569,7 +569,7 @@ Future<GlobalKey<MediaContextMenuState>> _pumpPlexMovieMenu(
}),
);
final manager = MultiServerManager()..debugRegisterClientForTesting(client);
final multiServerProvider = MultiServerProvider(manager, DataAggregationService(manager));
final multiServerProvider = testMultiServerProvider(manager);
final stack = await ProfileStack.create(db: db, withStorage: false);
addTearDown(() async {
await stack.dispose();
@@ -771,7 +771,7 @@ Future<_SiblingMusicMenuHarness> _pumpSiblingMusicMenu(
await downloadProvider.ensureInitialized();
final client = _RelatedMusicClient(relatedItems);
final manager = MultiServerManager()..debugRegisterClientForTesting(client);
final multiServerProvider = MultiServerProvider(manager, DataAggregationService(manager));
final multiServerProvider = testMultiServerProvider(manager);
final stack = await ProfileStack.create(db: db, withStorage: false);
final music = _RecordingMusicPlaybackService();
final rootNavigatorKey = GlobalKey<NavigatorState>();
+2 -8
View File
@@ -14,8 +14,6 @@ import 'package:plezy/models/download_models.dart';
import 'package:plezy/profiles/active_profile_provider.dart';
import 'package:plezy/providers/download_provider.dart';
import 'package:plezy/providers/multi_server_provider.dart';
import 'package:plezy/services/data_aggregation_service.dart';
import 'package:plezy/services/multi_server_manager.dart';
import 'package:plezy/services/music/music_playback_service.dart';
import 'package:plezy/services/settings_service.dart';
import 'package:plezy/theme/mono_theme.dart';
@@ -26,6 +24,7 @@ import 'package:plezy/widgets/music/mini_player.dart';
import 'package:provider/provider.dart';
import '../../test_helpers/media_items.dart';
import '../../test_helpers/multi_server_fixtures.dart';
import '../../test_helpers/prefs.dart';
import '../../test_helpers/profile_stack.dart';
@@ -159,14 +158,9 @@ void main() {
ActiveProfileProvider? activeProfileProvider,
DownloadProvider? downloadProvider,
}) {
final manager = MultiServerManager();
final multiServerProvider = MultiServerProvider(manager, DataAggregationService(manager));
addTearDown(service.dispose);
addTearDown(observer.suppress.dispose);
addTearDown(() {
multiServerProvider.dispose();
manager.dispose();
});
final multiServerProvider = testMultiServer().provider;
return TranslationProvider(
child: MultiProvider(
+7 -13
View File
@@ -14,7 +14,6 @@ import 'package:plezy/navigation/navigation_tabs.dart';
import 'package:plezy/providers/hidden_libraries_provider.dart';
import 'package:plezy/providers/libraries_provider.dart';
import 'package:plezy/providers/multi_server_provider.dart';
import 'package:plezy/services/data_aggregation_service.dart';
import 'package:plezy/services/multi_server_manager.dart';
import 'package:plezy/services/settings_service.dart';
import 'package:plezy/utils/platform_detector.dart';
@@ -22,6 +21,7 @@ import 'package:plezy/widgets/app_icon.dart';
import 'package:plezy/widgets/side_navigation_rail.dart';
import 'package:provider/provider.dart';
import '../test_helpers/multi_server_fixtures.dart';
import '../test_helpers/prefs.dart';
import '../test_helpers/theme.dart';
@@ -82,8 +82,7 @@ Future<void> _pumpBasicRail(
addTearDown(hiddenLibrariesProvider.dispose);
final manager = MultiServerManager();
final aggregation = DataAggregationService(manager);
final multiServerProvider = MultiServerProvider(manager, aggregation);
final multiServerProvider = testMultiServerProvider(manager);
addTearDown(multiServerProvider.dispose);
final rail = SideNavigationRail(
@@ -140,8 +139,7 @@ void main() {
addTearDown(hiddenLibrariesProvider.dispose);
final manager = MultiServerManager();
final aggregation = DataAggregationService(manager);
final multiServerProvider = MultiServerProvider(manager, aggregation);
final multiServerProvider = testMultiServerProvider(manager);
addTearDown(multiServerProvider.dispose);
await tester.pumpWidget(
@@ -205,8 +203,7 @@ void main() {
addTearDown(hiddenLibrariesProvider.dispose);
final manager = MultiServerManager();
final aggregation = DataAggregationService(manager);
final multiServerProvider = MultiServerProvider(manager, aggregation);
final multiServerProvider = testMultiServerProvider(manager);
addTearDown(multiServerProvider.dispose);
await tester.pumpWidget(
@@ -309,8 +306,7 @@ void main() {
addTearDown(hiddenLibrariesProvider.dispose);
final manager = MultiServerManager();
final aggregation = DataAggregationService(manager);
final multiServerProvider = MultiServerProvider(manager, aggregation);
final multiServerProvider = testMultiServerProvider(manager);
addTearDown(multiServerProvider.dispose);
final reports = <bool>[];
@@ -373,8 +369,7 @@ void main() {
addTearDown(hiddenLibrariesProvider.dispose);
final manager = MultiServerManager();
final aggregation = DataAggregationService(manager);
final multiServerProvider = MultiServerProvider(manager, aggregation);
final multiServerProvider = testMultiServerProvider(manager);
addTearDown(multiServerProvider.dispose);
final sideNavKey = GlobalKey<SideNavigationRailState>();
@@ -450,8 +445,7 @@ void main() {
addTearDown(hiddenLibrariesProvider.dispose);
final manager = MultiServerManager();
final aggregation = DataAggregationService(manager);
final multiServerProvider = MultiServerProvider(manager, aggregation);
final multiServerProvider = testMultiServerProvider(manager);
addTearDown(multiServerProvider.dispose);
final sideNavKey = GlobalKey<SideNavigationRailState>();
+33 -33
View File
@@ -10,7 +10,6 @@ import 'package:plezy/media/media_hub.dart';
import 'package:plezy/media/media_item.dart';
import 'package:plezy/media/media_kind.dart';
import 'package:plezy/providers/multi_server_provider.dart';
import 'package:plezy/services/data_aggregation_service.dart';
import 'package:plezy/services/device_performance.dart';
import 'package:plezy/services/multi_server_manager.dart';
import 'package:plezy/services/settings_service.dart';
@@ -25,6 +24,7 @@ import 'package:provider/provider.dart';
import '../test_helpers/prefs.dart';
import '../test_helpers/media_items.dart';
import '../test_helpers/multi_server_fixtures.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
@@ -346,7 +346,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: InputModeTracker(
child: MaterialApp(
theme: monoTheme(dark: true),
@@ -418,7 +418,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: InputModeTracker(
child: MaterialApp(
theme: monoTheme(dark: true),
@@ -473,7 +473,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: theme,
home: Scaffold(
@@ -513,7 +513,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -557,7 +557,7 @@ void main() {
Widget rail(MediaHub hub) {
return ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -599,7 +599,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: InputModeTracker(
child: MaterialApp(
theme: monoTheme(dark: true),
@@ -689,7 +689,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: InputModeTracker(
child: MaterialApp(
theme: monoTheme(dark: true),
@@ -762,7 +762,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: InputModeTracker(
child: MaterialApp(
theme: monoTheme(dark: true),
@@ -822,7 +822,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: InputModeTracker(
child: MaterialApp(
theme: monoTheme(dark: true),
@@ -884,7 +884,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -939,7 +939,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1001,7 +1001,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1039,7 +1039,7 @@ void main() {
Widget buildRail(List<MediaHub> hubs, {String? initialHubId, String? initialItemId, bool autofocus = false}) {
final serverManager = MultiServerManager();
return ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1081,7 +1081,7 @@ void main() {
Widget buildRail(List<MediaHub> hubs, {String? initialHubId}) {
final serverManager = MultiServerManager();
return ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1124,7 +1124,7 @@ void main() {
Widget buildRail(List<MediaHub> hubs, {String? initialItemId}) {
final serverManager = MultiServerManager();
return ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1204,7 +1204,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1318,7 +1318,7 @@ void main() {
Widget buildRail(List<MediaHub> hubs) {
return ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1401,7 +1401,7 @@ void main() {
Widget buildRail({required bool backgroundLoaded}) {
return ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1477,7 +1477,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1579,7 +1579,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1655,7 +1655,7 @@ void main() {
final serverManager = MultiServerManager();
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1744,7 +1744,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1835,7 +1835,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1907,7 +1907,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -1972,7 +1972,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -2023,7 +2023,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -2071,7 +2071,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -2127,7 +2127,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -2178,7 +2178,7 @@ void main() {
final item = testMediaItem(id: 'item_1', backend: MediaBackend.plex, kind: MediaKind.movie, title: 'Movie');
final hub = MediaHub(id: 'hub_1', title: 'Hub', type: 'movie', items: [item], size: 1);
return ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -2213,7 +2213,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -2258,7 +2258,7 @@ void main() {
await tester.pumpWidget(
ChangeNotifierProvider<MultiServerProvider>(
create: (_) => MultiServerProvider(serverManager, DataAggregationService(serverManager)),
create: (_) => testMultiServerProvider(serverManager),
child: MaterialApp(
theme: monoTheme(dark: true),
home: Scaffold(
@@ -2289,7 +2289,7 @@ void main() {
testWidgets('background bleed updates do not renotify rail focus', (tester) async {
final serverManager = MultiServerManager();
final multiServerProvider = MultiServerProvider(serverManager, DataAggregationService(serverManager));
final multiServerProvider = testMultiServerProvider(serverManager);
addTearDown(multiServerProvider.dispose);
final focusedItemIds = <String>[];