···1414/// Shows reposts on the left with a green repost icon, and likes on the right
1515/// with a pink heart icon. Only renders if there are actual interactions.
1616class KnownInteractionsBar extends StatelessWidget {
1717- const KnownInteractionsBar({
1818- required this.interactions,
1919- super.key,
2020- });
1717+ const KnownInteractionsBar({required this.interactions, super.key});
21182219 /// List of known interactions to display.
2320 final List<KnownInteraction>? interactions;
···75727673/// A single interaction pill with icon and avatar stack.
7774class _InteractionPill extends StatelessWidget {
7878- const _InteractionPill({
7979- required this.icon,
8080- required this.avatars,
8181- });
7575+ const _InteractionPill({required this.icon, required this.avatars});
82768377 final Widget icon;
8478 final List<AvatarData> avatars;
···10498 children: [
10599 icon,
106100 const SizedBox(width: 6),
107107- AvatarStack(
108108- avatars: avatars,
109109- largeSize: 32,
110110- ),
101101+ AvatarStack(avatars: avatars, largeSize: 32),
111102 ],
112103 ),
113104 ),
···37373838 /// Dark theme text theme
3939 /// Same typography but optimized for dark backgrounds
4040- static TextTheme get dark => light.apply(
4141- bodyColor: Colors.white,
4242- displayColor: Colors.white,
4343- );
4040+ static TextTheme get dark =>
4141+ light.apply(bodyColor: Colors.white, displayColor: Colors.white);
4442}
···44/// Result returned from the video editor containing the edited video
55/// and optional metadata about audio used.
66class VideoEditorResult {
77- const VideoEditorResult({
88- required this.video,
99- this.soundRef,
1010- });
77+ const VideoEditorResult({required this.video, this.soundRef});
118129 /// The edited video file.
1310 final XFile video;
···77/// like advancing to the next post without needing to pass callbacks through
88/// the widget tree.
99class FeedActionController {
1010- FeedActionController({
1111- required this.onAdvanceAndRemove,
1212- });
1010+ FeedActionController({required this.onAdvanceAndRemove});
13111412 /// Called after an action that should remove the current post and advance
1513 /// to the next one (e.g., blocking a user, deleting a post).
···4341 FeedActionControllerNotifier,
4442 FeedActionController?,
4543 Feed
4646- >(
4747- (ref, feed) => FeedActionControllerNotifier(),
4848- );
4444+ >((ref, feed) => FeedActionControllerNotifier());
···170170171171 for (final newLabel in allLabels) {
172172 final uri = AtUri.parse(newLabel.uri);
173173- extraInfo.update(
174174- uri,
175175- (value) {
176176- final existingLabels = value.postLabels;
173173+ extraInfo.update(uri, (value) {
174174+ final existingLabels = value.postLabels;
177175178178- // if new label in existing labels,
179179- //check if it should replace existing one
180180- if (existingLabels.any((label) => label.val == newLabel.val)) {
181181- final existingLabel = existingLabels.firstWhere(
182182- (label) => label.val == newLabel.val,
183183- );
176176+ // if new label in existing labels,
177177+ //check if it should replace existing one
178178+ if (existingLabels.any((label) => label.val == newLabel.val)) {
179179+ final existingLabel = existingLabels.firstWhere(
180180+ (label) => label.val == newLabel.val,
181181+ );
184182185185- // if new label says that existing one is negated or expired,
186186- // replace the existing one
187187- if (((newLabel.ver ?? 0) > (existingLabel.ver ?? 0) &&
188188- newLabel.isNeg) ||
189189- existingLabel.exp != null &&
190190- existingLabel.exp!.isBefore(DateTime.now())) {
191191- existingLabels.remove(existingLabel);
192192- return (
193193- postLabels: [...existingLabels, newLabel],
194194- );
195195- } else {
196196- // if the new label is the same as the existing one, do nothing
197197- return value;
198198- }
183183+ // if new label says that existing one is negated or expired,
184184+ // replace the existing one
185185+ if (((newLabel.ver ?? 0) > (existingLabel.ver ?? 0) &&
186186+ newLabel.isNeg) ||
187187+ existingLabel.exp != null &&
188188+ existingLabel.exp!.isBefore(DateTime.now())) {
189189+ existingLabels.remove(existingLabel);
190190+ return (postLabels: [...existingLabels, newLabel]);
199191 } else {
200200- // if the new label is not in the existing labels, add it
201201- return (
202202- postLabels: [...existingLabels, newLabel],
203203- );
192192+ // if the new label is the same as the existing one, do nothing
193193+ return value;
204194 }
205205- },
206206- ifAbsent: () => (
207207- postLabels: [newLabel],
208208- ),
209209- );
195195+ } else {
196196+ // if the new label is not in the existing labels, add it
197197+ return (postLabels: [...existingLabels, newLabel]);
198198+ }
199199+ }, ifAbsent: () => (postLabels: [newLabel]));
210200 }
211201212202 final filteredPosts = await _filterHiddenPosts(
···8181 /// Fetch missing profile data using ActorRepository (Spark-first with
8282 /// Bluesky fallback)
8383 /// Only fetches profiles that are actually incomplete (missing key fields)
8484- Future<void> _fetchAndMergeProfiles(
8585- List<ProfileView> profiles,
8686- ) async {
8484+ Future<void> _fetchAndMergeProfiles(List<ProfileView> profiles) async {
8785 // Check if profile is incomplete - need to check multiple fields
8886 // Profile is incomplete if it's missing displayName, description, or avatar
8987 // AND has a valid handle (if handle missing, it's likely a deleted account)
···285285 // Check if post is from Bluesky
286286 final isBskyPost = post.uri.collection
287287 .toString()
288288- .startsWith(
289289- 'app.bsky',
290290- );
288288+ .startsWith('app.bsky');
291289 // Otherwise, navigate to the new profile
292290 context.router.push(
293291 ProfileRoute(