···
1
-
import 'package:cached_network_image/cached_network_image.dart';
2
-
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
4
+
import '../../constants/app_colors.dart';
import '../../models/post.dart';
import '../../providers/auth_provider.dart';
import '../../providers/feed_provider.dart';
8
+
import '../../widgets/post_card.dart';
class FeedScreen extends StatefulWidget {
const FeedScreen({super.key});
···
final isLoadingMore = context.select<FeedProvider, bool>(
77
+
final currentTime = context.select<FeedProvider, DateTime?>(
78
+
(p) => p.currentTime,
79
-
backgroundColor: const Color(0xFF0B0F14),
82
+
backgroundColor: AppColors.background,
81
-
backgroundColor: const Color(0xFF0B0F14),
82
-
foregroundColor: Colors.white,
84
+
backgroundColor: AppColors.background,
85
+
foregroundColor: AppColors.textPrimary,
title: Text(isAuthenticated ? 'Feed' : 'Explore'),
automaticallyImplyLeading: false,
···
isLoadingMore: isLoadingMore,
isAuthenticated: isAuthenticated,
96
+
currentTime: currentTime,
···
required List<FeedViewPost> posts,
required bool isLoadingMore,
required bool isAuthenticated,
108
+
required DateTime? currentTime,
// Loading state (only show full-screen loader for initial load,
if (isLoading && posts.isEmpty) {
109
-
child: CircularProgressIndicator(color: Color(0xFFFF6B35)),
114
+
child: CircularProgressIndicator(color: AppColors.primary),
···
126
-
color: Color(0xFFFF6B35),
131
+
color: AppColors.primary,
const SizedBox(height: 16),
133
-
color: Colors.white,
138
+
color: AppColors.textPrimary,
fontWeight: FontWeight.bold,
const SizedBox(height: 8),
_getUserFriendlyError(error),
140
-
style: const TextStyle(fontSize: 14, color: Color(0xFFB6C2D2)),
145
+
style: const TextStyle(
147
+
color: AppColors.textSecondary,
textAlign: TextAlign.center,
const SizedBox(height: 24),
146
-
Provider.of<FeedProvider>(
154
+
Provider.of<FeedProvider>(context, listen: false).retry();
style: ElevatedButton.styleFrom(
152
-
backgroundColor: const Color(0xFFFF6B35),
157
+
backgroundColor: AppColors.primary,
child: const Text('Retry'),
···
mainAxisAlignment: MainAxisAlignment.center,
170
-
const Icon(Icons.forum, size: 64, color: Color(0xFFFF6B35)),
175
+
const Icon(Icons.forum, size: 64, color: AppColors.primary),
const SizedBox(height: 24),
isAuthenticated ? 'No posts yet' : 'No posts to discover',
176
-
color: Colors.white,
181
+
color: AppColors.textPrimary,
fontWeight: FontWeight.bold,
···
? 'Subscribe to communities to see posts in your feed'
: 'Check back later for new posts',
185
-
style: const TextStyle(fontSize: 14, color: Color(0xFFB6C2D2)),
190
+
style: const TextStyle(
192
+
color: AppColors.textSecondary,
textAlign: TextAlign.center,
···
197
-
color: const Color(0xFFFF6B35),
205
+
color: AppColors.primary,
controller: _scrollController,
// Add extra item for loading indicator or pagination error
202
-
posts.length + (isLoadingMore || error != null ? 1 : 0),
209
+
itemCount: posts.length + (isLoadingMore || error != null ? 1 : 0),
itemBuilder: (context, index) {
// Footer: loading indicator or error message
if (index == posts.length) {
···
padding: EdgeInsets.all(16),
211
-
child: CircularProgressIndicator(color: Color(0xFFFF6B35)),
218
+
child: CircularProgressIndicator(color: AppColors.primary),
···
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
221
-
color: const Color(0xFF1A1F26),
228
+
color: AppColors.background,
borderRadius: BorderRadius.circular(8),
223
-
border: Border.all(color: const Color(0xFFFF6B35)),
230
+
border: Border.all(color: AppColors.primary),
229
-
color: Color(0xFFFF6B35),
236
+
color: AppColors.primary,
const SizedBox(height: 8),
_getUserFriendlyError(error),
236
-
color: Color(0xFFB6C2D2),
243
+
color: AppColors.textSecondary,
textAlign: TextAlign.center,
···
const SizedBox(height: 12),
244
-
Provider.of<FeedProvider>(
251
+
Provider.of<FeedProvider>(context, listen: false)
style: TextButton.styleFrom(
252
-
foregroundColor: const Color(0xFFFF6B35),
256
+
foregroundColor: AppColors.primary,
child: const Text('Retry'),
···
final post = posts[index];
264
-
label: 'Feed post in ${post.post.community.name} by '
269
+
'Feed post in ${post.post.community.name} by '
'${post.post.author.displayName ?? post.post.author.handle}. '
'${post.post.title ?? ""}',
268
-
child: _PostCard(post: post),
273
+
child: PostCard(post: post, currentTime: currentTime),
···
return 'Something went wrong. Please try again';
301
-
class _PostCard extends StatelessWidget {
303
-
const _PostCard({required this.post});
304
-
final FeedViewPost post;
307
-
Widget build(BuildContext context) {
309
-
margin: const EdgeInsets.only(bottom: 8),
310
-
decoration: const BoxDecoration(
311
-
color: Color(0xFF1A1F26),
312
-
border: Border(bottom: BorderSide(color: Color(0xFF2A2F36))),
315
-
padding: const EdgeInsets.all(16),
317
-
crossAxisAlignment: CrossAxisAlignment.start,
319
-
// Community and author info
322
-
// Community avatar placeholder
326
-
decoration: BoxDecoration(
327
-
color: const Color(0xFFFF6B35),
328
-
borderRadius: BorderRadius.circular(4),
332
-
post.post.community.name[0].toUpperCase(),
333
-
style: const TextStyle(
334
-
color: Colors.white,
336
-
fontWeight: FontWeight.bold,
341
-
const SizedBox(width: 8),
344
-
crossAxisAlignment: CrossAxisAlignment.start,
347
-
'c/${post.post.community.name}',
348
-
style: const TextStyle(
349
-
color: Colors.white,
351
-
fontWeight: FontWeight.bold,
355
-
'Posted by ${post.post.author.displayName ?? ''
356
-
'${post.post.author.handle}'}',
357
-
style: const TextStyle(
358
-
color: Color(0xFFB6C2D2),
367
-
const SizedBox(height: 12),
370
-
if (post.post.title != null) ...[
373
-
style: const TextStyle(
374
-
color: Colors.white,
376
-
fontWeight: FontWeight.bold,
379
-
const SizedBox(height: 12),
382
-
// Embed (link preview)
383
-
if (post.post.embed?.external != null) ...[
384
-
_EmbedCard(embed: post.post.embed!.external!),
385
-
const SizedBox(height: 12),
392
-
Icons.arrow_upward,
394
-
color: Colors.white.withValues(alpha: 0.6),
396
-
const SizedBox(width: 4),
398
-
'${post.post.stats.score}',
400
-
color: Colors.white.withValues(alpha: 0.6),
404
-
const SizedBox(width: 16),
406
-
Icons.comment_outlined,
408
-
color: Colors.white.withValues(alpha: 0.6),
410
-
const SizedBox(width: 4),
412
-
'${post.post.stats.commentCount}',
414
-
color: Colors.white.withValues(alpha: 0.6),
427
-
class _EmbedCard extends StatelessWidget {
429
-
const _EmbedCard({required this.embed});
430
-
final ExternalEmbed embed;
433
-
Widget build(BuildContext context) {
434
-
// Only show image if thumbnail exists
435
-
if (embed.thumb == null) {
436
-
return const SizedBox.shrink();
440
-
decoration: BoxDecoration(
441
-
borderRadius: BorderRadius.circular(8),
442
-
border: Border.all(color: const Color(0xFF2A2F36)),
444
-
clipBehavior: Clip.antiAlias,
445
-
child: CachedNetworkImage(
446
-
imageUrl: embed.thumb!,
447
-
width: double.infinity,
450
-
placeholder: (context, url) => Container(
451
-
width: double.infinity,
453
-
color: const Color(0xFF1A1F26),
454
-
child: const Center(
455
-
child: CircularProgressIndicator(color: Color(0xFF484F58)),
458
-
errorWidget: (context, url, error) {
460
-
debugPrint('❌ Image load error: $error');
461
-
debugPrint('URL: $url');
464
-
width: double.infinity,
466
-
color: const Color(0xFF1A1F26),
468
-
Icons.broken_image,
469
-
color: Color(0xFF484F58),