feat(models): add ViewerState to PostView for vote tracking

Add ViewerState class to represent the viewer's relationship with posts:
- vote: direction ("up", "down", or null)
- voteUri: AT-URI of the vote record
- saved: bookmark status
- tags: user-applied tags

This enables the feed to include viewer-specific state from the backend,
allowing proper initialization of vote UI state on feed refresh.

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

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

Changed files
+50 -2
lib
+9 -2
lib/models/comment.dart
··· 156 156 } 157 157 158 158 class CommentViewerState { 159 - CommentViewerState({this.vote}); 159 + CommentViewerState({this.vote, this.voteUri}); 160 160 161 161 factory CommentViewerState.fromJson(Map<String, dynamic> json) { 162 - return CommentViewerState(vote: json['vote'] as String?); 162 + return CommentViewerState( 163 + vote: json['vote'] as String?, 164 + voteUri: json['voteUri'] as String?, 165 + ); 163 166 } 164 167 168 + /// Vote direction: "up", "down", or null if not voted 165 169 final String? vote; 170 + 171 + /// AT-URI of the vote record (if backend provides it) 172 + final String? voteUri; 166 173 }
+41
lib/models/post.dart
··· 47 47 final FeedReason? reason; 48 48 } 49 49 50 + class ViewerState { 51 + ViewerState({ 52 + this.vote, 53 + this.voteUri, 54 + this.saved = false, 55 + this.savedUri, 56 + this.tags, 57 + }); 58 + 59 + factory ViewerState.fromJson(Map<String, dynamic> json) { 60 + return ViewerState( 61 + vote: json['vote'] as String?, 62 + voteUri: json['voteUri'] as String?, 63 + saved: json['saved'] as bool? ?? false, 64 + savedUri: json['savedUri'] as String?, 65 + tags: (json['tags'] as List<dynamic>?)?.cast<String>(), 66 + ); 67 + } 68 + 69 + /// Vote direction: "up", "down", or null if not voted 70 + final String? vote; 71 + 72 + /// AT-URI of the vote record 73 + final String? voteUri; 74 + 75 + /// Whether the post is saved/bookmarked 76 + final bool saved; 77 + 78 + /// AT-URI of the saved record 79 + final String? savedUri; 80 + 81 + /// User-applied tags 82 + final List<String>? tags; 83 + } 84 + 50 85 class PostView { 51 86 PostView({ 52 87 required this.uri, ··· 61 96 required this.stats, 62 97 this.embed, 63 98 this.facets, 99 + this.viewer, 64 100 }); 65 101 66 102 factory PostView.fromJson(Map<String, dynamic> json) { ··· 87 123 .map((f) => PostFacet.fromJson(f as Map<String, dynamic>)) 88 124 .toList() 89 125 : null, 126 + viewer: 127 + json['viewer'] != null 128 + ? ViewerState.fromJson(json['viewer'] as Map<String, dynamic>) 129 + : null, 90 130 ); 91 131 } 92 132 final String uri; ··· 101 141 final PostStats stats; 102 142 final PostEmbed? embed; 103 143 final List<PostFacet>? facets; 144 + final ViewerState? viewer; 104 145 } 105 146 106 147 class AuthorView {