···
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
4
+
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../constants/app_colors.dart';
···
/// - Periodic updates of time strings
/// - Deterministic testing without DateTime.now()
class PostCard extends StatelessWidget {
28
-
const PostCard({required this.post, this.currentTime, super.key});
32
+
this.showCommentButton = true,
33
+
this.disableNavigation = false,
final DateTime? currentTime;
39
+
final bool showCommentButton;
40
+
final bool disableNavigation;
42
+
/// Check if this post should be clickable
43
+
/// Only text posts (no embeds or non-video/link embeds) are clickable
44
+
bool get _isClickable {
45
+
// If navigation is explicitly disabled (e.g., on detail screen), not clickable
46
+
if (disableNavigation) {
50
+
final embed = post.post.embed;
52
+
// If no embed, it's a text-only post - clickable
53
+
if (embed == null) {
57
+
// If embed exists, check if it's a video or link type
58
+
final external = embed.external;
59
+
if (external == null) {
60
+
return true; // No external embed, clickable
63
+
final embedType = external.embedType;
65
+
// Video and video-stream posts should NOT be clickable (they have their own tap handling)
66
+
if (embedType == 'video' || embedType == 'video-stream') {
70
+
// Link embeds should NOT be clickable (they have their own link handling)
71
+
if (embedType == 'link') {
75
+
// All other types are clickable
79
+
void _navigateToDetail(BuildContext context) {
80
+
// Navigate to post detail screen
81
+
// Use URI-encoded version of the post URI for the URL path
82
+
// Pass the full post object via extras
83
+
final encodedUri = Uri.encodeComponent(post.post.uri);
84
+
context.push('/post/$encodedUri', extra: post);
Widget build(BuildContext context) {
···
const SizedBox(height: 8),
85
-
if (post.post.title != null) ...[
88
-
style: const TextStyle(
89
-
color: AppColors.textPrimary,
91
-
fontWeight: FontWeight.w400,
138
+
// Wrap content in InkWell if clickable (text-only posts)
141
+
onTap: () => _navigateToDetail(context),
143
+
crossAxisAlignment: CrossAxisAlignment.start,
146
+
if (post.post.title != null) ...[
149
+
style: const TextStyle(
150
+
color: AppColors.textPrimary,
152
+
fontWeight: FontWeight.w400,
157
+
// Spacing after title (only if we have text)
158
+
if (post.post.title != null && post.post.text.isNotEmpty)
159
+
const SizedBox(height: 8),
161
+
// Post text body preview
162
+
if (post.post.text.isNotEmpty) ...[
164
+
padding: const EdgeInsets.all(10),
165
+
decoration: BoxDecoration(
166
+
color: AppColors.backgroundSecondary,
167
+
borderRadius: BorderRadius.circular(8),
172
+
color: AppColors.textPrimary.withValues(alpha: 0.7),
177
+
overflow: TextOverflow.ellipsis,
185
+
// Non-clickable content (video/link posts)
187
+
crossAxisAlignment: CrossAxisAlignment.start,
190
+
if (post.post.title != null) ...[
193
+
style: const TextStyle(
194
+
color: AppColors.textPrimary,
196
+
fontWeight: FontWeight.w400,
96
-
// Spacing after title (only if we have content below)
97
-
if (post.post.title != null &&
98
-
(post.post.embed?.external != null ||
99
-
post.post.text.isNotEmpty))
100
-
const SizedBox(height: 8),
201
+
// Spacing after title (only if we have content below)
202
+
if (post.post.title != null &&
203
+
(post.post.embed?.external != null ||
204
+
post.post.text.isNotEmpty))
205
+
const SizedBox(height: 8),
102
-
// Embed (link preview)
103
-
if (post.post.embed?.external != null) ...[
105
-
embed: post.post.embed!.external!,
106
-
streamableService: context.read<StreamableService>(),
108
-
const SizedBox(height: 8),
207
+
// Embed (link preview)
208
+
if (post.post.embed?.external != null) ...[
210
+
embed: post.post.embed!.external!,
211
+
streamableService: context.read<StreamableService>(),
213
+
const SizedBox(height: 8),
111
-
// Post text body preview
112
-
if (post.post.text.isNotEmpty) ...[
114
-
padding: const EdgeInsets.all(10),
115
-
decoration: BoxDecoration(
116
-
color: AppColors.backgroundSecondary,
117
-
borderRadius: BorderRadius.circular(8),
122
-
color: AppColors.textPrimary.withValues(alpha: 0.7),
127
-
overflow: TextOverflow.ellipsis,
216
+
// Post text body preview
217
+
if (post.post.text.isNotEmpty) ...[
219
+
padding: const EdgeInsets.all(10),
220
+
decoration: BoxDecoration(
221
+
color: AppColors.backgroundSecondary,
222
+
borderRadius: BorderRadius.circular(8),
227
+
color: AppColors.textPrimary.withValues(alpha: 0.7),
232
+
overflow: TextOverflow.ellipsis,
// External link (if present)
if (post.post.embed?.external != null) ...[
···
const SizedBox(height: 4),
142
-
PostCardActions(post: post),
251
+
showCommentButton: showCommentButton,