diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-22 17:07:15 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-22 17:07:15 +0300 |
| commit | 71b01ca5a9f511f6e7c814c3ee173592a7ec011e (patch) | |
| tree | 248c118034a30f78cc69d7d90454c730cf5d2688 /player-android/lib/utils | |
| parent | 32aa3946cc1a0f5166ecff59dc229d0c02c7e9b6 (diff) | |
Implement API token management screen in Settings (task hb)
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>
Diffstat (limited to 'player-android/lib/utils')
| -rw-r--r-- | player-android/lib/utils/error_mappers.dart | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/player-android/lib/utils/error_mappers.dart b/player-android/lib/utils/error_mappers.dart index 6e6f118..23b0b51 100644 --- a/player-android/lib/utils/error_mappers.dart +++ b/player-android/lib/utils/error_mappers.dart @@ -419,6 +419,34 @@ String adminTrashErrorMessage(Object error) { return 'Unexpected error. Please try again.'; } +/// Maps any thrown object from [PlayerApiClient.listAPITokens], +/// [PlayerApiClient.createAPIToken], or [PlayerApiClient.revokeAPIToken] to a +/// human-readable UI string. +/// +/// Adds messages for the common failure modes: +/// - 400: the request body is invalid (e.g. empty name, non-positive expiry). +/// Delegates to [dioErrorMessage] to surface the server's JSON body message. +/// - 404: the token no longer exists (may have been revoked already). +/// +/// Kept as a separate top-level function (Open-Closed, DRY) so it can evolve +/// independently of the other mappers. +String apiTokenErrorMessage(Object error) { + if (error is DioException) { + if (error.response?.statusCode == 404) { + return 'Token not found. It may have already been revoked.'; + } + if (error.response?.statusCode == 400) { + // Prefer the server's JSON body message (e.g. "name required") over a + // generic fallback so the user knows exactly what to fix. + final serverMsg = dioErrorMessage(error); + if (!serverMsg.startsWith('Server error')) return serverMsg; + return 'Invalid request. Check the token name and expiry and try again.'; + } + return dioConnectionErrorMessage(error); + } + return 'Unexpected error. Please try again.'; +} + /// Maps any thrown object from [PlayerApiClient.downloadEpisode] to a UI string. /// /// Adds human-readable messages for the failure modes specific to triggering a |
