From d40a585b5848cc5bfe788c372a2bd1c028b9308d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 21 May 2026 23:22:57 +0300 Subject: Implement FolderBrowserScreen with browseSet endpoint and breadcrumb navigation (va) Adds a FolderBrowserScreen that calls browseSet on init and pull-to-refresh, renders subfolders first (with cover via setFolderCoverUrl) then media items, and provides a scrollable breadcrumb bar for navigating the folder hierarchy. Key changes: - player-android/lib/screens/folder_browser_screen.dart: new screen with loading/empty/error/refresh states, generation-counter cancellation, and no Dio import in the screen layer (DIP). - player-android/lib/api/player_api_client.dart: add setFolderCoverUrl() so screens never access rawDio directly for URL construction (DIP). - player-android/lib/utils/duration_formatter.dart: extract shared formatDuration() from MediaGridScreen to eliminate the DRY violation. - player-android/lib/screens/media_grid_screen.dart: delegate to formatDuration() from the shared utility. - player-android/lib/utils/error_mappers.dart: add folderErrorMessage(). - player-android/lib/app_routes.dart: add folderBrowser and folderBrowserPath(). - player-android/lib/router.dart: wire /browse/:setId GoRoute. - player-android/test/screens/folder_browser_screen_test.dart: 16 widget tests covering renders, breadcrumbs, folder/media tap navigation, empty, error, retry, and pull-to-refresh. All 298 tests pass; flutter analyze reports no issues. Co-Authored-By: Claude Sonnet 4.6 --- player-android/lib/router.dart | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'player-android/lib/router.dart') diff --git a/player-android/lib/router.dart b/player-android/lib/router.dart index 03f14bc..170cfd5 100644 --- a/player-android/lib/router.dart +++ b/player-android/lib/router.dart @@ -18,6 +18,7 @@ import 'screens/settings_screen.dart'; import 'screens/share_screen.dart'; import 'screens/my_shares_screen.dart'; import 'screens/notes_editor_screen.dart'; +import 'screens/folder_browser_screen.dart'; import 'screens/video_player_screen.dart'; // Re-export AppRoutes so existing callers that import router.dart for routes @@ -187,6 +188,27 @@ final routerProvider = Provider((ref) { path: AppRoutes.shares, builder: (context, state) => const MySharesScreen(), ), + GoRoute( + // Folder browser — shows subfolders and media at the current path + // within a set. The ':setId' path segment identifies the set; + // the optional 'path' query parameter identifies the current subfolder + // (absent or empty means root). + path: AppRoutes.folderBrowser, + builder: (context, state) { + final raw = state.pathParameters['setId']!; + final setId = int.tryParse(raw) ?? 0; + final path = state.uri.queryParameters['path']; + // setName is optionally passed as a String extra so the screen can + // show the set name in the app bar without an extra API call. + final setName = + state.extra is String ? state.extra as String : null; + return FolderBrowserScreen( + setId: setId, + path: path, + setName: setName, + ); + }, + ), ], ); }); -- cgit v1.2.3