From c2944be8708f4bb9c687679b4ed63e1398b83f05 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 22 May 2026 17:17:23 +0300 Subject: Fix review issues for API token management screen (task hb) - 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 --- player-android/lib/screens/api_tokens_screen.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'player-android/lib') diff --git a/player-android/lib/screens/api_tokens_screen.dart b/player-android/lib/screens/api_tokens_screen.dart index df57cf6..5a94ac5 100644 --- a/player-android/lib/screens/api_tokens_screen.dart +++ b/player-android/lib/screens/api_tokens_screen.dart @@ -70,8 +70,8 @@ class _TokenRow { /// is inserted at [_tokens!.length] before the API call; on success the /// real row replaces that slot; on error the slot is removed. /// - Revoke uses identity-based optimistic removal: -/// `_tokens!.removeWhere((t) => t.id == token.id)` first, then reverted -/// with `[..._tokens!, token]` on error (consistent with MySharesScreen). +/// `_tokens!.removeWhere((t) => t.id == token.id)` first, then appended +/// back on error (mirrors AdminUsersScreen; append avoids unsafe index-based re-insert). /// - The plaintext token from `createAPIToken` is shown exactly once in a /// dialog with a copy button; after the user taps Done it is discarded. /// - All async continuations guard on [mounted] to prevent setState / context @@ -229,7 +229,7 @@ class _ApiTokensScreenState extends ConsumerState { /// /// Identity-based optimistic removal: the token row is removed from the list /// immediately, then the API call is made. On error the row is appended back - /// (consistent with MySharesScreen and the task spec). + /// (mirrors AdminUsersScreen, which also appends on revert). Future _revokeToken(_TokenRow token) async { final confirmed = await _confirmRevoke(token.name); if (!confirmed || !mounted) return; @@ -250,8 +250,8 @@ class _ApiTokensScreenState extends ConsumerState { } catch (e) { if (!mounted) return; // Re-append the token to restore the list after the failed revoke. - // Append rather than re-insert at original index to avoid position jitter - // from concurrent mutations (mirrors MySharesScreen and admin_users). + // Append rather than re-insert at original index to match AdminUsersScreen; + // index-based re-insert is unsafe if concurrent loads replace _tokens. setState(() => _tokens = [...?_tokens, token]); _showError(apiTokenErrorMessage(e)); } -- cgit v1.2.3