summaryrefslogtreecommitdiff
path: root/player-android/test
AgeCommit message (Collapse)Author
2026-05-22Fix review issues for API token management screen (task hb)Paul Buetow
- Make `Key('api_tokens_copy_snackbar')` const (promoted by outer const SnackBar) - Fix misleading comment in _revokeToken: mirrors AdminUsersScreen (append on revert), not MySharesScreen (which uses index-based re-insert) - Add widget test: submits null expiresInDays when no expiry date selected - Add unit tests for expiresInDays clamp logic (correct days, min 1, max 36500) - Skip 403 handling in apiTokenErrorMessage: token endpoints use requireSession middleware and the service layer never returns ErrForbidden for token ops Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Implement API token management screen in Settings (task hb)Paul Buetow
Adds ApiTokensScreen (/settings/api-tokens) so any authenticated user can list, create (with optional expiry), and revoke their own Bearer API tokens. The plaintext token is displayed exactly once after creation with a clipboard copy button. Create and revoke use optimistic UI with proper revert on error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Fix review issues and add widget tests for admin screens (eb)Paul Buetow
- Fix typo _isTriggerring → _isTriggering in AdminRescanScreen - Replace index-based removeAt with identity-based removeWhere in AdminTrashScreen - Add .cast<T>() for list results from Future.wait in AdminPermissionsScreen - Add tap-outside safety comment in _confirmHardDelete - Fix broken dartdoc reference [RescanScreen] → [AdminRescanScreen] - Add widget tests for AdminRescanScreen, AdminTrashScreen, AdminPermissionsScreen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Fix review issues in AdminUsersScreen and related files (task db)Paul Buetow
- Replace identity-equality optimistic-create revert with index-based logic (placeholderIdx) to avoid relying on reference equality - Replace insert(index) delete revert with append to avoid stale-index position jitter from concurrent mutations - Remove _RoleBadge key collision; update test to use text finders - currentUserProvider catch block returns null instead of User(id:0) to avoid colliding with the optimistic placeholder sentinel; fix broken comment - adminUserErrorMessage 400-branch delegates to dioErrorMessage to eliminate duplicated JSON body-parsing logic - Add Completer-backed optimistic placeholder visibility test - _EmptyView: replace magic height SizedBox with LayoutBuilder+Center - _CreateUserDialogState: inline _buildForm and _buildActions into build() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Implement AdminUsersScreen with create/delete user and admin gating (db)Paul Buetow
- Add AdminUsersScreen with user list, create dialog, delete confirmation, and optimistic UI (revert on error) for both create and delete operations. - Add currentUserProvider (FutureProvider) to resolve the logged-in User object from token storage + listUsers, used for self-delete gating and Settings admin section visibility. - Gate Admin section (Manage Users tile) in SettingsScreen behind currentUserProvider → isAdmin, providing defence-in-depth alongside server-side 403 enforcement. - Add adminUserErrorMessage to error_mappers.dart with 400/403/409 handling. - Add adminUsers route constant (AppRoutes.adminUsers) and GoRouter entry. - Add 25 tests in admin_users_screen_test.dart and 3 admin-section tests in settings_screen_test.dart (397 tests total pass). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Fix gb final review: SizedBox.shrink footer and shares_no_more testPaul Buetow
Replace Text('') with SizedBox.shrink() in _buildFooter so dead space and the premature episodes_no_more key are eliminated when more pages exist. Add testWidgets for the shares_no_more footer key. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Fix gb review issues: _isLoadingMore stuck on refresh, empty footer space, ↵Paul Buetow
pagination tests - Reset _isLoadingMore = false inside _load()'s setState in both MediaGridScreen and PodcastEpisodesScreen, so a generation-mismatch early return in an in-flight _loadMore does not leave the spinner permanently stuck after a pull-to-refresh (major bug fix). - Replace the empty Text('') with SizedBox.shrink() in _buildFooterSliver when hasMore=true and isLoadingMore=false to eliminate the 32px dead space (nit fix). - Add three pagination widget tests to each screen: _loadMore appends a second page, _loadMore is a no-op while already in-flight, and pull-to-refresh while _loadMore is in-flight leaves _isLoadingMore=false after _load completes (regression coverage for the major bug). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Add widget test for double-tap download guard in PodcastEpisodesScreenPaul Buetow
Adds a Completer<Media> field to _FakeApiClient so tests can hold a download in-flight, and a new test that taps the download button twice while the first call is pending to verify _pendingDownloads suppresses the second API call. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Add play and download buttons to PodcastEpisodesScreen (bb)Paul Buetow
Each episode row now shows a play button (when mediaId is non-null) that navigates to AudioPlayerScreen, or a download button (when mediaId is null) that triggers a server-side download via downloadEpisode. On success the row updates in-place to swap the download button for a play button without requiring a full page reload. Added episodeDownloadErrorMessage to error_mappers.dart and extended the test suite to cover all new paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Fix final review nits for Material 3 theming (task fb)Paul Buetow
- Remove redundant multiSelectionEnabled: false from SegmentedButton - DRY up theme toggle tests via optional themeNotifier param in _pumpSettingsScreen - Add UI assertion to verify button reflects ThemeMode.dark after tap Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Fix Material 3 theming review issues (task fb)Paul Buetow
- Roll back ThemeNotifier state on SharedPreferences write failure so in-memory and disk never diverge - Fix stale comment: scaffoldBackgroundColor ← --bg-body (not background) - Fix source attribution: theme.css not docs/theming.md - Make _ThemeToggle const-constructible; remove incorrect ignore comment - Inline _buildSegmentedButton into _ThemeToggle.build (was a trivial passthrough) - Add defensive isNotEmpty comment on SegmentedButton.onSelectionChanged - Hoist buildLightTheme()/buildDarkTheme() to module-level finals in main.dart so ThemeData is built once at startup rather than on every rebuild - Add _ThemeToggle tests: initial segment selection and segment tap dispatch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22Implement public ShareViewerScreen with Android deep-link intent-filter (9b)Paul Buetow
- New ShareViewerScreen (/share/:token): unauthenticated share-viewer that fetches share metadata via publicApiClientProvider, renders filename, type, duration, thumbnail, and a Play button routing to the video/audio player. - New publicApiClientProvider: bare Dio client (no auth interceptors) for the public share endpoint; shares kPlayerBaseUrl with the authenticated client. - AndroidManifest: http + https deep-link intent-filters for /share/.* so Android routes share URLs directly into the app (App Links / autoVerify). - router.dart: /share/:token bypasses the authentication redirect; guard uses AppRoutes.shareViewerPrefix constant instead of a raw '/share/' string (DIP). - app_routes.dart: shareViewer route constant, shareViewerPrefix, shareViewerPath helper. - error_mappers.dart: shareViewerErrorMessage — 404 invalid/revoked, 410 expired. - player_api_client.dart: baseUrl getter encapsulates rawDio.options.baseUrl so screens never access transport internals directly (ISP, DIP). - Review fixes: OCP icon map in _FallbackThumbnail, LSP explicit baseUrl overrides in test fakes, DIP shareViewerPrefix constant. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement offline progress queue with sqflite and connectivity_plus (3b)Paul Buetow
Introduces ProgressQueue backed by SQLite for offline-capable progress tracking, with ProgressQueueBase (LSP+DIP), databaseFactory injection (DIP), and ProgressSyncClient narrow interface (ISP). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement podcast episode played/unplayed toggle with progress bar (cb)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement FolderBrowserScreen with browseSet endpoint and breadcrumb ↵Paul Buetow
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 <noreply@anthropic.com>
2026-05-21Implement MySharesScreen with revoke/copy-link actions reachable from ↵Paul Buetow
Settings (8b) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement all remaining PlayerApiClient stubs and add unit testsPaul Buetow
Implements every previously-unimplemented method in DioPlayerApiClient: shares (listSharesForMedia, listMyShares, revokeShare), public shared-media endpoints (getSharedMediaPage, streamSharedMedia, getSharedThumbnail, downloadSharedMedia), config (getConfig), sets (getSetCover, updateSetCover, uploadToSet), media helpers (regenerateThumbnail, deleteMedia, restoreMedia), progress batch (batchUpdateProgress), podcasts (listPodcasts, listEpisodes, downloadEpisode, toggleEpisodeComplete), admin users/permissions/scanner (listUsers, createUser, deleteUser, listPermissions, grantPermission, revokePermission, triggerRescan, getScanProgress, listTrash), and API tokens (listAPITokens, createAPIToken, revokeAPIToken). Adds listAPITokens/createAPIToken/revokeAPIToken/batchUpdateProgress to the abstract PlayerApiClient. Corrects listPermissions return type from List<Map> to Map<String,dynamic> to match the server's single-object response. Adds 18 new unit tests covering shares (listMyShares success/empty/401, revokeShare success/404/401) and podcasts (listEpisodes success/pagination/ empty/401, toggleEpisodeComplete success/404/401). All 264 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement NotesEditorScreen with auto-save debounce and clear confirmation (6b)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement Tag picker with optimistic UI, autocomplete, and delete chips on ↵Paul Buetow
MediaDetailScreen (5b) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Wrap AudioPlayerScreen in audio_service for background playback (1b)Paul Buetow
Creates PlayerAudioHandler (BaseAudioHandler + SeekHandler) that wraps just_audio's AudioPlayer and bridges it to the Android media session: background foreground-service playback, lock-screen / notification controls (play/pause/seek/skip ±15 s), audio focus, and Bluetooth headset events all handled by audio_service. Key design decisions: - Handler registered once via AudioService.init in main() and injected into ProviderScope via overrideWithValue (DIP: no global mutable var). - Progress-sync timer stays in the screen so PlayerApiClient is never imported by the handler (SRP boundary preserved). - Seek bar onChanged routes through handler.seek() so the notification position updates on slider drags (Law of Demeter fix). - _initPlayer refactored into _buildAuthHeaders / _loadSource / _resumeFromSavedPosition helpers (each ≤30 lines, SoC). - Tests override audioHandlerProvider with _FakePlayerAudioHandler to avoid platform-channel calls; all 221 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement ContinueWatchingScreen with resume cards and progress routing (2b)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement favorites toggle with optimistic UI in MediaDetailScreen and ↵Paul Buetow
MediaGridScreen (4b) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement SearchFilterBar with debounce, type/favorites/sort filters for ↵Paul Buetow
MediaGridScreen (wa) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement PodcastListScreen and SubscribeDialog with podcast feed management ↵Paul Buetow
(ab) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement CreateShareDialog with expiry/max-uses, shareUrl abstraction (7b)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement AudioPlayerScreen with just_audio, progress sync, and bearer auth (0b)Paul Buetow
Replaces the placeholder AudioPlayerScreen with a full implementation: - Streams audio via just_audio AudioPlayer with Bearer token in headers - Progress sync mirrors VideoPlayerScreen exactly: 5 s timer, isPlaying guard, 95 % finished threshold, _finishedEmitted guard, same dispose order - UI: cover art placeholder, StreamBuilder-backed seek bar, play/pause, skip ±15 s (fast_rewind/fast_forward icons), speed selector (0.5–2x) - Resume from server-saved position via getMediaProgress on init - Error state with retry button; all async continuations guard on mounted - Widget tests cover: loading spinner, error view structure, AppBar title, URL resolution; audio_session channel mocked to unblock headless tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement VideoPlayerScreen with chewie, progress sync, and bearer auth (za)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement MediaDetailScreen with metadata, favorite toggle, player routing (ya)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement MediaGridScreen with grid, loading/empty/error states, and widget ↵Paul Buetow
tests (ua) - Replace placeholder MediaGridScreen with a full implementation: loads media via listMedia(setId:), renders a Material 3 2-column grid of thumbnail cards with filename, type icon, and duration overlay. - Add thumbnailUrl(int mediaId) to PlayerApiClient so screen files construct thumbnail URLs without importing Dio (DIP). - Add mediaErrorMessage() top-level helper to error_mappers.dart (Open-Closed Principle; matches setsErrorMessage pattern). - Update router.dart to forward the set name as a route extra so MediaGridScreen shows the name in the app bar without extra API calls. - Update home_screen.dart _SetCard tap to pass set name as route extra. - Add 10 widget tests covering: loading, grid render, tap navigation to /media/:id, empty state, error state + retry, and pull-to-refresh. - All 109 tests pass; flutter analyze reports no issues. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement SetsListScreen with Material 3 grid, pull-to-refresh, and widget ↵Paul Buetow
tests (ta) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement SettingsScreen with AuthGuard and settings persistence (sa)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Add GET /api/v1/auth/count endpoint and first-run routing in Android appPaul Buetow
Server: expose a public countUsers endpoint (GET /api/v1/auth/count) so mobile clients can detect first-run (count=0) without a session. Android: wire countUsers via DioPlayerApiClient, add firstRunProvider (FutureProvider), update go_router redirect to drive /bootstrap vs /login based on the count, rework LoginScreen to handle loading/error states, and add widget tests for the new login screen and smoke-test updates. Fix review issues: correct FutureProvider cache-lifetime comment in first_run_provider.dart; add TestServer_CountUsers covering zero-users and users-exist cases to handlers_test.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement BootstrapScreen with widget tests (qa)Paul Buetow
Adds the first-run admin-account setup screen (POST /api/v1/auth/bootstrap), wires it into the go_router redirect guard, and extracts the pure _dioErrorMessage helper to a top-level function to satisfy SRP. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20Implement DioPlayerApiClient with real HTTP bodies for core API methods (oa)Paul Buetow
Replace UnimplementedError stubs with concrete Dio calls for bootstrap, login, logout, listSets, browseSet, listMedia, getMedia, streamMedia, downloadMedia, getThumbnail, healthz, and readyz. Wire DioPlayerApiClient into the Riverpod provider. Add http_mock_adapter dev dependency and 25 unit tests covering success + error paths for all implemented methods. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20Add DioClient with auth interceptors and refactor PlayerApiClient (ma)Paul Buetow
Introduces dio_client.dart with _AuthInterceptor (guards Bearer token injection with containsKey so callers can override Authorization) and _UnauthorizedInterceptor (private fields, 401 → login redirect). PlayerApiClient is refactored to accept a pre-configured Dio instance instead of raw credentials; adds dio + flutter_secure_storage deps. Fixes review issues: Auth header comment/behavior corrected, private fields on _UnauthorizedInterceptor, unused import removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20Fix PlayerApiClient const constructor and Media.tags deserialization (d9+e9)Paul Buetow
- Remove const from PlayerApiClient constructor (Uri is not const-constructable) - Replace .cast<String>() with .whereType<String>().toList() in Media.fromJson to silently drop non-string and null tag elements instead of throwing TypeError - Add regression test: tags [1, 'valid', null] deserialized as ['valid'] Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20Guard dateTimeFromJson against malformed date strings (c9)Paul Buetow
Wrap DateTime.parse in try/catch so a malformed or non-ISO-8601 date string from the server degrades to null instead of throwing a FormatException that crashes model deserialization across Media, MediaSet, User, PodcastFeed, PodcastEpisode, PlaybackHint, Share, and Note. Add player-android/test/json_helpers_test.dart with 11 tests covering null, empty, non-string, valid ISO, and malformed inputs (regression guard for the original crash). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18Add named routes and widget smoke tests to player-androidPaul Buetow
Adds HomeScreen ('/') and NowPlayingScreen ('/now-playing') as the first two named routes in PlayerAndroidApp, replacing the anonymous home widget. Widget smoke tests verify that the app starts on the library screen, navigates to the now-playing screen on button tap, and back-navigates correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18Add Dart unit tests for models and API client constructorPaul Buetow
Add fromJson/toJson round-trip tests for all 9 Dart models (Media, MediaSet, User, Tag, Note, Share, PodcastFeed, PodcastEpisode, PlaybackHint) and 3 PlayerApiClient constructor tests. Each model group covers a fully-populated round-trip (including int-to-double cast verification and ISO-8601 DateTime value comparison) and an exhaustive defaults-from-empty-map test. All 21 tests run via 'flutter test' with no emulator. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>