feat(feed): initialize vote state from viewer data in feed response

- Remove VoteService dependency - vote state comes from feed response
- Replace getUserVotes + loadInitialVotes with per-post setInitialVoteState
- Use viewer.vote and viewer.voteUri from backend response
- Call setInitialVoteState for ALL posts to handle cross-device vote removal

This fixes the bug where votes would disappear on feed refresh:
1. Backend now populates viewer state from PDS cache
2. Feed provider initializes VoteProvider state from viewer data
3. Score adjustments are cleared to prevent double-counting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+13 -18
lib
providers
+13 -18
lib/providers/feed_provider.dart
···
import 'package:flutter/foundation.dart';
import '../models/post.dart';
import '../services/coves_api_service.dart';
-
import '../services/vote_service.dart';
import 'auth_provider.dart';
import 'vote_provider.dart';
···
this._authProvider, {
CovesApiService? apiService,
VoteProvider? voteProvider,
-
VoteService? voteService,
-
}) : _voteProvider = voteProvider,
-
_voteService = voteService {
// Use injected service (for testing) or create new one (for production)
// Pass token getter, refresh handler, and sign out handler to API service
// for automatic fresh token retrieval and automatic token refresh on 401
···
final AuthProvider _authProvider;
late final CovesApiService _apiService;
final VoteProvider? _voteProvider;
-
final VoteService? _voteService;
// Track previous auth state to detect transitions
bool _wasAuthenticated = false;
···
debugPrint('✅ $feedName loaded: ${_posts.length} posts total');
}
-
// Load initial vote state from PDS (only if authenticated)
-
if (_authProvider.isAuthenticated &&
-
_voteProvider != null &&
-
_voteService != null) {
-
try {
-
final userVotes = await _voteService.getUserVotes();
-
_voteProvider.loadInitialVotes(userVotes);
-
} on Exception catch (e) {
-
if (kDebugMode) {
-
debugPrint('⚠️ Failed to load vote state: $e');
-
}
-
// Don't fail the feed load if vote loading fails
-
// Keep silent per PR review discussion
}
}
} on Exception catch (e) {
···
import 'package:flutter/foundation.dart';
import '../models/post.dart';
import '../services/coves_api_service.dart';
import 'auth_provider.dart';
import 'vote_provider.dart';
···
this._authProvider, {
CovesApiService? apiService,
VoteProvider? voteProvider,
+
}) : _voteProvider = voteProvider {
// Use injected service (for testing) or create new one (for production)
// Pass token getter, refresh handler, and sign out handler to API service
// for automatic fresh token retrieval and automatic token refresh on 401
···
final AuthProvider _authProvider;
late final CovesApiService _apiService;
final VoteProvider? _voteProvider;
// Track previous auth state to detect transitions
bool _wasAuthenticated = false;
···
debugPrint('✅ $feedName loaded: ${_posts.length} posts total');
}
+
// Initialize vote state from viewer data in feed response
+
// IMPORTANT: Call setInitialVoteState for ALL feed items, even when
+
// viewer.vote is null. This ensures that if a user removed their vote
+
// on another device, the local state is cleared on refresh.
+
if (_authProvider.isAuthenticated && _voteProvider != null) {
+
for (final feedItem in response.feed) {
+
final viewer = feedItem.post.viewer;
+
_voteProvider.setInitialVoteState(
+
postUri: feedItem.post.uri,
+
voteDirection: viewer?.vote,
+
voteUri: viewer?.voteUri,
+
);
}
}
} on Exception catch (e) {