|
1 | 1 | import 'package:flutter/material.dart'; |
2 | | -import 'package:go_router/go_router.dart'; |
3 | | -import 'package:ht_main/entity_details/view/entity_details_page.dart'; |
| 2 | +import 'package:go_router/go_router.dart'; // Added |
| 3 | +import 'package:ht_main/entity_details/models/entity_type.dart'; |
| 4 | +import 'package:ht_main/entity_details/view/entity_details_page.dart'; // Added for Page Arguments |
4 | 5 | import 'package:ht_main/l10n/app_localizations.dart'; |
5 | 6 | import 'package:ht_main/l10n/l10n.dart'; |
6 | | -import 'package:ht_main/router/routes.dart'; |
7 | | -import 'package:ht_shared/ht_shared.dart'; |
| 7 | +import 'package:ht_main/router/routes.dart'; // Added |
| 8 | +import 'package:ht_shared/ht_shared.dart' show Headline; |
8 | 9 | import 'package:ht_ui_kit/ht_ui_kit.dart'; |
9 | 10 | // timeago import removed from here, handled by utility |
10 | 11 |
|
@@ -32,7 +33,7 @@ class HeadlineTileImageStart extends StatelessWidget { |
32 | 33 | final Widget? trailing; |
33 | 34 |
|
34 | 35 | /// The type of the entity currently being viewed in detail (e.g., on a category page). |
35 | | - final ContentType? currentContextEntityType; |
| 36 | + final EntityType? currentContextEntityType; |
36 | 37 |
|
37 | 38 | /// The ID of the entity currently being viewed in detail. |
38 | 39 | final String? currentContextEntityId; |
@@ -61,154 +62,32 @@ class HeadlineTileImageStart extends StatelessWidget { |
61 | 62 | height: 72, |
62 | 63 | child: ClipRRect( |
63 | 64 | borderRadius: BorderRadius.circular(AppSpacing.xs), |
64 | | - child: Image.network( |
65 | | - headline.imageUrl, |
66 | | - fit: BoxFit.cover, |
67 | | - loadingBuilder: (context, child, loadingProgress) { |
68 | | - if (loadingProgress == null) return child; |
69 | | - return ColoredBox( |
70 | | - color: colorScheme.surfaceContainerHighest, |
71 | | - child: const Center( |
72 | | - child: CircularProgressIndicator(strokeWidth: 2), |
73 | | - ), |
74 | | - ); |
75 | | - }, |
76 | | - errorBuilder: (context, error, stackTrace) => ColoredBox( |
77 | | - color: colorScheme.surfaceContainerHighest, |
78 | | - child: Icon( |
79 | | - Icons.broken_image_outlined, |
80 | | - color: colorScheme.onSurfaceVariant, |
81 | | - size: AppSpacing.xl, |
82 | | - ), |
83 | | - ), |
84 | | - ), |
85 | | - ), |
86 | | - ), |
87 | | - const SizedBox(width: AppSpacing.md), |
88 | | - Expanded( |
89 | | - child: Column( |
90 | | - crossAxisAlignment: CrossAxisAlignment.start, |
91 | | - children: [ |
92 | | - Text( |
93 | | - headline.title, |
94 | | - style: textTheme.titleMedium |
95 | | - ?.copyWith(fontWeight: FontWeight.w500), |
96 | | - maxLines: 2, |
97 | | - overflow: TextOverflow.ellipsis, |
98 | | - ), |
99 | | - const SizedBox(height: AppSpacing.sm), |
100 | | - _HeadlineMetadataRow( |
101 | | - headline: headline, |
102 | | - l10n: l10n, |
103 | | - colorScheme: colorScheme, |
104 | | - textTheme: textTheme, |
105 | | - currentContextEntityType: currentContextEntityType, |
106 | | - currentContextEntityId: currentContextEntityId, |
107 | | - ), |
108 | | - ], |
109 | | - ), |
110 | | - ), |
111 | | - if (trailing != null) ...[ |
112 | | - const SizedBox(width: AppSpacing.sm), |
113 | | - trailing!, |
114 | | - ], |
115 | | - ], |
116 | | - ), |
117 | | - ), |
118 | | - ), |
119 | | - ); |
120 | | - } |
121 | | -} |
122 | | - |
123 | | -/// Private helper widget to build the metadata row. |
124 | | -class _HeadlineMetadataRow extends StatelessWidget { |
125 | | - const _HeadlineMetadataRow({ |
126 | | - required this.headline, |
127 | | - required this.l10n, |
128 | | - required this.colorScheme, |
129 | | - required this.textTheme, |
130 | | - this.currentContextEntityType, |
131 | | - this.currentContextEntityId, |
132 | | - }); |
133 | | - |
134 | | - final Headline headline; |
135 | | - final AppLocalizations l10n; |
136 | | - final ColorScheme colorScheme; |
137 | | - final TextTheme textTheme; |
138 | | - final ContentType? currentContextEntityType; |
139 | | - final String? currentContextEntityId; |
140 | | - |
141 | | - @override |
142 | | - Widget build(BuildContext context) { |
143 | | - // TODO(all): Use a utility function for relative time formatting. |
144 | | - final formattedDate = ''; // formatRelativeTime(context, headline.createdAt); |
145 | | - |
146 | | - final metadataTextStyle = textTheme.bodySmall?.copyWith( |
147 | | - color: colorScheme.primary.withOpacity(0.7), |
148 | | - ); |
149 | | - final iconColor = colorScheme.primary.withOpacity(0.7); |
150 | | - const iconSize = AppSpacing.sm; |
151 | | - |
152 | | - return Wrap( |
153 | | - spacing: AppSpacing.sm, |
154 | | - runSpacing: AppSpacing.xs, |
155 | | - crossAxisAlignment: WrapCrossAlignment.center, |
156 | | - children: [ |
157 | | - if (formattedDate.isNotEmpty) |
158 | | - Row( |
159 | | - mainAxisSize: MainAxisSize.min, |
160 | | - children: [ |
161 | | - Icon( |
162 | | - Icons.calendar_today_outlined, |
163 | | - size: iconSize, |
164 | | - color: iconColor, |
165 | | - ), |
166 | | - const SizedBox(width: AppSpacing.xs / 2), |
167 | | - Text(formattedDate, style: metadataTextStyle), |
168 | | - ], |
169 | | - ), |
170 | | - if (headline.topic.name.isNotEmpty && |
171 | | - !(currentContextEntityType == ContentType.topic && |
172 | | - headline.topic.id == currentContextEntityId)) ...[ |
173 | | - if (formattedDate.isNotEmpty) |
174 | | - Padding( |
175 | | - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xs), |
176 | | - child: Text('•', style: metadataTextStyle), |
177 | | - ), |
178 | | - GestureDetector( |
179 | | - onTap: () { |
180 | | - context.push( |
181 | | - Routes.topicDetails, |
182 | | - extra: EntityDetailsPageArguments(entity: headline.topic), |
183 | | - ); |
184 | | - }, |
185 | | - child: Text(headline.topic.name, style: metadataTextStyle), |
186 | | - ), |
187 | | - ], |
188 | | - if (!(currentContextEntityType == ContentType.source && |
189 | | - headline.source.id == currentContextEntityId)) ...[ |
190 | | - if (formattedDate.isNotEmpty || |
191 | | - (headline.topic.name.isNotEmpty && |
192 | | - !(currentContextEntityType == ContentType.topic && |
193 | | - headline.topic.id == currentContextEntityId))) |
194 | | - Padding( |
195 | | - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xs), |
196 | | - child: Text('•', style: metadataTextStyle), |
197 | | - ), |
198 | | - GestureDetector( |
199 | | - onTap: () { |
200 | | - context.push( |
201 | | - Routes.sourceDetails, |
202 | | - extra: EntityDetailsPageArguments(entity: headline.source), |
203 | | - ); |
204 | | - }, |
205 | | - child: Text(headline.source.name, style: metadataTextStyle), |
206 | | - ), |
207 | | - ], |
208 | | - ], |
209 | | - ); |
210 | | - } |
211 | | -} |
| 65 | + child: headline.imageUrl != null |
| 66 | + ? Image.network( |
| 67 | + headline.imageUrl, |
| 68 | + fit: BoxFit.cover, |
| 69 | + loadingBuilder: (context, child, loadingProgress) { |
| 70 | + if (loadingProgress == null) return child; |
| 71 | + return ColoredBox( |
| 72 | + color: colorScheme.surfaceContainerHighest, |
| 73 | + child: const Center( |
| 74 | + child: CircularProgressIndicator( |
| 75 | + strokeWidth: 2, |
| 76 | + ), |
| 77 | + ), |
| 78 | + ); |
| 79 | + }, |
| 80 | + errorBuilder: (context, error, stackTrace) => |
| 81 | + ColoredBox( |
| 82 | + color: colorScheme.surfaceContainerHighest, |
| 83 | + child: Icon( |
| 84 | + Icons.broken_image_outlined, |
| 85 | + color: colorScheme.onSurfaceVariant, |
| 86 | + size: AppSpacing.xl, |
| 87 | + ), |
| 88 | + ), |
| 89 | + ) |
| 90 | + : ColoredBox( |
212 | 91 | color: colorScheme.surfaceContainerHighest, |
213 | 92 | child: Icon( |
214 | 93 | Icons.image_not_supported_outlined, |
|
0 commit comments