Skip to content

Commit b5a81d0

Browse files
authored
Merge pull request #28 from headlines-toolkit/feature_headline_tile_ui_choice
Feature headline tile UI choice
2 parents df7a547 + dc39a49 commit b5a81d0

18 files changed

+1104
-352
lines changed

lib/account/view/saved_headlines_page.dart

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:go_router/go_router.dart'; // Added GoRouter
34
import 'package:ht_main/account/bloc/account_bloc.dart';
4-
import 'package:ht_main/headlines-feed/widgets/headline_item_widget.dart';
5+
import 'package:ht_main/app/bloc/app_bloc.dart'; // Added AppBloc
6+
// HeadlineItemWidget import removed
57
import 'package:ht_main/l10n/l10n.dart';
68
import 'package:ht_main/router/routes.dart';
7-
import 'package:ht_main/shared/widgets/widgets.dart';
9+
import 'package:ht_main/shared/shared.dart'; // Imports new headline tiles
10+
import 'package:ht_shared/ht_shared.dart'
11+
show Headline, HeadlineImageStyle; // Added HeadlineImageStyle
812

913
/// {@template saved_headlines_page}
1014
/// Displays the list of headlines saved by the user.
@@ -68,20 +72,64 @@ class SavedHeadlinesPage extends StatelessWidget {
6872
separatorBuilder: (context, index) => const Divider(height: 1),
6973
itemBuilder: (context, index) {
7074
final headline = savedHeadlines[index];
71-
return HeadlineItemWidget(
72-
headline: headline,
73-
targetRouteName: Routes.accountArticleDetailsName,
74-
trailing: IconButton(
75-
// Changed from trailingWidget
76-
icon: const Icon(Icons.delete_outline),
77-
tooltip: 'Remove from saved', // Placeholder
78-
onPressed: () {
79-
context.read<AccountBloc>().add(
80-
AccountSaveHeadlineToggled(headline: headline),
81-
);
82-
},
83-
),
75+
final imageStyle =
76+
context
77+
.watch<AppBloc>()
78+
.state
79+
.settings
80+
.feedPreferences
81+
.headlineImageStyle;
82+
83+
final trailingButton = IconButton(
84+
icon: const Icon(Icons.delete_outline),
85+
tooltip: l10n.headlineDetailsRemoveFromSavedTooltip, // Use l10n
86+
onPressed: () {
87+
context.read<AccountBloc>().add(
88+
AccountSaveHeadlineToggled(headline: headline),
89+
);
90+
},
8491
);
92+
93+
Widget tile;
94+
switch (imageStyle) {
95+
case HeadlineImageStyle.hidden:
96+
tile = HeadlineTileTextOnly(
97+
headline: headline,
98+
onHeadlineTap:
99+
() => context.goNamed(
100+
Routes.accountArticleDetailsName,
101+
pathParameters: {'id': headline.id},
102+
extra: headline,
103+
),
104+
trailing: trailingButton,
105+
);
106+
break;
107+
case HeadlineImageStyle.smallThumbnail:
108+
tile = HeadlineTileImageStart(
109+
headline: headline,
110+
onHeadlineTap:
111+
() => context.goNamed(
112+
Routes.accountArticleDetailsName,
113+
pathParameters: {'id': headline.id},
114+
extra: headline,
115+
),
116+
trailing: trailingButton,
117+
);
118+
break;
119+
case HeadlineImageStyle.largeThumbnail:
120+
tile = HeadlineTileImageTop(
121+
headline: headline,
122+
onHeadlineTap:
123+
() => context.goNamed(
124+
Routes.accountArticleDetailsName,
125+
pathParameters: {'id': headline.id},
126+
extra: headline,
127+
),
128+
trailing: trailingButton,
129+
);
130+
break;
131+
}
132+
return tile;
85133
},
86134
);
87135
},

lib/app/view/app.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ class _AppViewState extends State<_AppView> {
182182
previous.flexScheme != current.flexScheme ||
183183
previous.fontFamily != current.fontFamily ||
184184
previous.appTextScaleFactor != current.appTextScaleFactor ||
185-
previous.locale != current.locale, // Added locale check
185+
previous.locale != current.locale ||
186+
previous.settings != current.settings, // Added settings check
186187
builder: (context, state) {
187188
print('[_AppViewState] Building MaterialApp.router');
188189
print('[_AppViewState] state.fontFamily: ${state.fontFamily}');

lib/headline-details/view/headline_details_page.dart

Lines changed: 97 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ import 'package:flutter/foundation.dart' show kIsWeb; // Import kIsWeb
55
import 'package:flutter/material.dart';
66
import 'package:flutter_bloc/flutter_bloc.dart';
77
import 'package:go_router/go_router.dart'; // Import GoRouter
8-
import 'package:ht_main/account/bloc/account_bloc.dart'; // Import AccountBloc
9-
import 'package:ht_main/headline-details/bloc/headline_details_bloc.dart'; // Import BLoC
10-
import 'package:ht_main/headline-details/bloc/similar_headlines_bloc.dart'; // Import SimilarHeadlinesBloc
11-
import 'package:ht_main/headlines-feed/widgets/headline_item_widget.dart'; // Import HeadlineItemWidget
8+
import 'package:ht_main/account/bloc/account_bloc.dart';
9+
import 'package:ht_main/app/bloc/app_bloc.dart'; // Added AppBloc
10+
import 'package:ht_main/headline-details/bloc/headline_details_bloc.dart';
11+
import 'package:ht_main/headline-details/bloc/similar_headlines_bloc.dart';
12+
// HeadlineItemWidget import removed
1213
import 'package:ht_main/l10n/l10n.dart';
13-
import 'package:ht_main/router/routes.dart'; // Import Routes
14+
import 'package:ht_main/router/routes.dart';
1415
import 'package:ht_main/shared/shared.dart';
1516
import 'package:ht_shared/ht_shared.dart'
16-
show Headline; // Import Headline model
17+
show Headline, HeadlineImageStyle; // Added HeadlineImageStyle
1718
import 'package:intl/intl.dart';
1819
import 'package:share_plus/share_plus.dart'; // Import share_plus
1920
import 'package:url_launcher/url_launcher_string.dart';
@@ -268,45 +269,59 @@ class _HeadlineDetailsPageState extends State<HeadlineDetailsPage> {
268269
child: Text(headline.title, style: textTheme.headlineMedium),
269270
),
270271
),
271-
if (headline.imageUrl != null)
272-
SliverPadding(
273-
padding: const EdgeInsets.only(
274-
top: AppSpacing.lg,
275-
left: AppSpacing.paddingLarge,
276-
right: AppSpacing.paddingLarge,
277-
),
278-
sliver: SliverToBoxAdapter(
279-
child: ClipRRect(
280-
borderRadius: BorderRadius.circular(AppSpacing.md),
281-
child: Image.network(
282-
headline.imageUrl!,
283-
width: double.infinity,
284-
height: 200,
285-
fit: BoxFit.cover,
286-
loadingBuilder: (context, child, loadingProgress) {
287-
if (loadingProgress == null) return child;
288-
return Container(
289-
width: double.infinity,
290-
height: 200,
291-
color: colorScheme.surfaceContainerHighest,
292-
child: const Center(child: CircularProgressIndicator()),
293-
);
294-
},
295-
errorBuilder:
296-
(context, error, stackTrace) => Container(
272+
// Image or Placeholder Section
273+
SliverPadding(
274+
padding: const EdgeInsets.only(
275+
top: AppSpacing.lg,
276+
left: AppSpacing.paddingLarge,
277+
right: AppSpacing.paddingLarge,
278+
),
279+
sliver: SliverToBoxAdapter(
280+
child: ClipRRect(
281+
borderRadius: BorderRadius.circular(AppSpacing.md),
282+
child:
283+
headline.imageUrl != null
284+
? Image.network(
285+
headline.imageUrl!,
286+
width: double.infinity,
287+
height: 200,
288+
fit: BoxFit.cover,
289+
loadingBuilder: (context, child, loadingProgress) {
290+
if (loadingProgress == null) return child;
291+
return Container(
292+
width: double.infinity,
293+
height: 200,
294+
color: colorScheme.surfaceContainerHighest,
295+
child: const Center(
296+
child: CircularProgressIndicator(),
297+
),
298+
);
299+
},
300+
errorBuilder:
301+
(context, error, stackTrace) => Container(
302+
width: double.infinity,
303+
height: 200,
304+
color: colorScheme.surfaceContainerHighest,
305+
child: Icon(
306+
Icons.broken_image_outlined,
307+
color: colorScheme.onSurfaceVariant,
308+
size: AppSpacing.xxl,
309+
),
310+
),
311+
)
312+
: Container(
297313
width: double.infinity,
298314
height: 200,
299315
color: colorScheme.surfaceContainerHighest,
300316
child: Icon(
301-
Icons.broken_image,
317+
Icons.image_not_supported_outlined,
302318
color: colorScheme.onSurfaceVariant,
303319
size: AppSpacing.xxl,
304320
),
305321
),
306-
),
307-
),
308322
),
309323
),
324+
),
310325
SliverPadding(
311326
padding: horizontalPadding.copyWith(top: AppSpacing.lg),
312327
sliver: SliverToBoxAdapter(
@@ -490,17 +505,54 @@ class _HeadlineDetailsPageState extends State<HeadlineDetailsPage> {
490505
horizontal: AppSpacing.paddingMedium,
491506
vertical: AppSpacing.sm,
492507
),
493-
child: HeadlineItemWidget(
494-
headline: similarHeadline,
495-
// Use the onTap callback for navigation
496-
onTap: (tappedHeadline) {
497-
context.pushNamed(
498-
Routes.articleDetailsName,
499-
pathParameters: {'id': tappedHeadline.id},
500-
extra: tappedHeadline,
501-
);
508+
child: Builder(
509+
// Use Builder to get a new context that can watch AppBloc
510+
builder: (context) {
511+
final imageStyle =
512+
context
513+
.watch<AppBloc>()
514+
.state
515+
.settings
516+
.feedPreferences
517+
.headlineImageStyle;
518+
Widget tile;
519+
switch (imageStyle) {
520+
case HeadlineImageStyle.hidden:
521+
tile = HeadlineTileTextOnly(
522+
headline: similarHeadline,
523+
onHeadlineTap:
524+
() => context.pushNamed(
525+
Routes.articleDetailsName,
526+
pathParameters: {'id': similarHeadline.id},
527+
extra: similarHeadline,
528+
),
529+
);
530+
break;
531+
case HeadlineImageStyle.smallThumbnail:
532+
tile = HeadlineTileImageStart(
533+
headline: similarHeadline,
534+
onHeadlineTap:
535+
() => context.pushNamed(
536+
Routes.articleDetailsName,
537+
pathParameters: {'id': similarHeadline.id},
538+
extra: similarHeadline,
539+
),
540+
);
541+
break;
542+
case HeadlineImageStyle.largeThumbnail:
543+
tile = HeadlineTileImageTop(
544+
headline: similarHeadline,
545+
onHeadlineTap:
546+
() => context.pushNamed(
547+
Routes.articleDetailsName,
548+
pathParameters: {'id': similarHeadline.id},
549+
extra: similarHeadline,
550+
),
551+
);
552+
break;
553+
}
554+
return tile;
502555
},
503-
// targetRouteName: Routes.articleDetailsName, // No longer needed here
504556
),
505557
);
506558
}, childCount: loadedState.similarHeadlines.length),

lib/headlines-feed/view/headlines_feed_page.dart

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import 'package:flutter_bloc/flutter_bloc.dart';
66
import 'package:go_router/go_router.dart';
77
// Import Category
88
// Import Country
9+
import 'package:flutter_bloc/flutter_bloc.dart';
10+
import 'package:go_router/go_router.dart';
11+
import 'package:ht_main/app/bloc/app_bloc.dart'; // Added to access settings
912
import 'package:ht_main/headlines-feed/bloc/headlines_feed_bloc.dart';
10-
import 'package:ht_main/headlines-feed/widgets/headline_item_widget.dart';
13+
// HeadlineItemWidget import removed
1114
import 'package:ht_main/l10n/l10n.dart';
1215
import 'package:ht_main/router/routes.dart';
1316
import 'package:ht_main/shared/shared.dart';
14-
// Import Source
17+
import 'package:ht_shared/ht_shared.dart'
18+
show Headline, HeadlineImageStyle; // Added HeadlineImageStyle
1519

1620
/// {@template headlines_feed_view}
1721
/// The core view widget for the headlines feed.
@@ -216,10 +220,51 @@ class _HeadlinesFeedPageState extends State<HeadlinesFeedPage> {
216220
}
217221
// Otherwise, build the headline item
218222
final headline = state.headlines[index];
219-
return HeadlineItemWidget(
220-
headline: headline,
221-
targetRouteName: Routes.articleDetailsName,
222-
);
223+
final imageStyle =
224+
context
225+
.watch<AppBloc>()
226+
.state
227+
.settings
228+
.feedPreferences
229+
.headlineImageStyle;
230+
231+
Widget tile;
232+
switch (imageStyle) {
233+
case HeadlineImageStyle.hidden:
234+
tile = HeadlineTileTextOnly(
235+
headline: headline,
236+
onHeadlineTap:
237+
() => context.goNamed(
238+
Routes.articleDetailsName,
239+
pathParameters: {'id': headline.id},
240+
extra: headline,
241+
),
242+
);
243+
break;
244+
case HeadlineImageStyle.smallThumbnail:
245+
tile = HeadlineTileImageStart(
246+
headline: headline,
247+
onHeadlineTap:
248+
() => context.goNamed(
249+
Routes.articleDetailsName,
250+
pathParameters: {'id': headline.id},
251+
extra: headline,
252+
),
253+
);
254+
break;
255+
case HeadlineImageStyle.largeThumbnail:
256+
tile = HeadlineTileImageTop(
257+
headline: headline,
258+
onHeadlineTap:
259+
() => context.goNamed(
260+
Routes.articleDetailsName,
261+
pathParameters: {'id': headline.id},
262+
extra: headline,
263+
),
264+
);
265+
break;
266+
}
267+
return tile;
223268
},
224269
),
225270
);

0 commit comments

Comments
 (0)