summaryrefslogtreecommitdiff
path: root/player-android/lib/utils
AgeCommit message (Collapse)Author
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-22Implement AdminPermissionsScreen, RescanScreen, and TrashScreen (eb)Paul Buetow
Add three admin-only screens with routes and navigation tiles: - AdminPermissionsScreen: permission matrix (users × sets) with optimistic grant/revoke checkboxes; admin rows shown as disabled (implicit access). - AdminRescanScreen: trigger library rescan, poll getScanProgress every 2s while running, cancel timer in dispose, show live file/set counters. - AdminTrashScreen: list soft-deleted media with restore + hard-delete (confirmation dialog); optimistic UI with revert-on-error for both actions. All three use generation counters for stale-async cancellation and guard async continuations on mounted. Added error mappers and route constants. 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 bb review issues: stale snapshot, double-tap guard, button dedup, commentsPaul Buetow
- _downloadEpisodeAt: read _episodes fresh inside setState instead of using the pre-await snapshot, and guard that the index is still valid and the row still lacks a mediaId, preventing silent overwrites of data refreshed by _load() during the await. - Add Set<int> _pendingDownloads to prevent concurrent download API calls when the user double-taps; visually disable the button while in-flight via new isLoading parameter on _DownloadButton. - Extract _EpisodeActionButton shared primitive to eliminate structural duplication between _PlayButton and _DownloadButton (DRY). - Fix misleading mediaId null-safety comment to describe the actual guarantee. - Add comments to episodeToggleErrorMessage and episodeDownloadErrorMessage explaining why they use action-specific fallback strings. 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-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 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 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-21Implement ContinueWatchingScreen with resume cards and progress routing (2b)Paul Buetow
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 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>