Skip to content

Commit 6a2c49a

Browse files
authored
Merge pull request #17 from headlines-toolkit/fix_data_fetching_in_content_management
Fix data fetching in content management
2 parents 6db818d + a476def commit 6a2c49a

11 files changed

+198
-75
lines changed

lib/content_management/bloc/content_management_bloc.dart

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:bloc/bloc.dart';
22
import 'package:equatable/equatable.dart';
33
import 'package:ht_data_repository/ht_data_repository.dart';
4+
import 'package:ht_dashboard/shared/constants/pagination_constants.dart';
45
import 'package:ht_shared/ht_shared.dart';
56

67
part 'content_management_event.dart';
@@ -60,14 +61,17 @@ class ContentManagementBloc
6061
) async {
6162
emit(state.copyWith(headlinesStatus: ContentManagementStatus.loading));
6263
try {
64+
final isPaginating = event.startAfterId != null;
65+
final previousHeadlines = isPaginating ? state.headlines : <Headline>[];
66+
6367
final paginatedHeadlines = await _headlinesRepository.readAll(
6468
startAfterId: event.startAfterId,
6569
limit: event.limit,
6670
);
6771
emit(
6872
state.copyWith(
6973
headlinesStatus: ContentManagementStatus.success,
70-
headlines: paginatedHeadlines.items,
74+
headlines: [...previousHeadlines, ...paginatedHeadlines.items],
7175
headlinesCursor: paginatedHeadlines.cursor,
7276
headlinesHasMore: paginatedHeadlines.hasMore,
7377
),
@@ -97,7 +101,9 @@ class ContentManagementBloc
97101
try {
98102
await _headlinesRepository.create(item: event.headline);
99103
// Reload headlines after creation
100-
add(const LoadHeadlinesRequested());
104+
add(
105+
const LoadHeadlinesRequested(limit: kDefaultRowsPerPage),
106+
);
101107
} on HtHttpException catch (e) {
102108
emit(
103109
state.copyWith(
@@ -123,7 +129,9 @@ class ContentManagementBloc
123129
try {
124130
await _headlinesRepository.update(id: event.id, item: event.headline);
125131
// Reload headlines after update
126-
add(const LoadHeadlinesRequested());
132+
add(
133+
const LoadHeadlinesRequested(limit: kDefaultRowsPerPage),
134+
);
127135
} on HtHttpException catch (e) {
128136
emit(
129137
state.copyWith(
@@ -149,7 +157,9 @@ class ContentManagementBloc
149157
try {
150158
await _headlinesRepository.delete(id: event.id);
151159
// Reload headlines after deletion
152-
add(const LoadHeadlinesRequested());
160+
add(
161+
const LoadHeadlinesRequested(limit: kDefaultRowsPerPage),
162+
);
153163
} on HtHttpException catch (e) {
154164
emit(
155165
state.copyWith(
@@ -173,14 +183,17 @@ class ContentManagementBloc
173183
) async {
174184
emit(state.copyWith(categoriesStatus: ContentManagementStatus.loading));
175185
try {
186+
final isPaginating = event.startAfterId != null;
187+
final previousCategories = isPaginating ? state.categories : <Category>[];
188+
176189
final paginatedCategories = await _categoriesRepository.readAll(
177190
startAfterId: event.startAfterId,
178191
limit: event.limit,
179192
);
180193
emit(
181194
state.copyWith(
182195
categoriesStatus: ContentManagementStatus.success,
183-
categories: paginatedCategories.items,
196+
categories: [...previousCategories, ...paginatedCategories.items],
184197
categoriesCursor: paginatedCategories.cursor,
185198
categoriesHasMore: paginatedCategories.hasMore,
186199
),
@@ -210,7 +223,9 @@ class ContentManagementBloc
210223
try {
211224
await _categoriesRepository.create(item: event.category);
212225
// Reload categories after creation
213-
add(const LoadCategoriesRequested());
226+
add(
227+
const LoadCategoriesRequested(limit: kDefaultRowsPerPage),
228+
);
214229
} on HtHttpException catch (e) {
215230
emit(
216231
state.copyWith(
@@ -236,7 +251,9 @@ class ContentManagementBloc
236251
try {
237252
await _categoriesRepository.update(id: event.id, item: event.category);
238253
// Reload categories after update
239-
add(const LoadCategoriesRequested());
254+
add(
255+
const LoadCategoriesRequested(limit: kDefaultRowsPerPage),
256+
);
240257
} on HtHttpException catch (e) {
241258
emit(
242259
state.copyWith(
@@ -262,7 +279,9 @@ class ContentManagementBloc
262279
try {
263280
await _categoriesRepository.delete(id: event.id);
264281
// Reload categories after deletion
265-
add(const LoadCategoriesRequested());
282+
add(
283+
const LoadCategoriesRequested(limit: kDefaultRowsPerPage),
284+
);
266285
} on HtHttpException catch (e) {
267286
emit(
268287
state.copyWith(
@@ -286,14 +305,17 @@ class ContentManagementBloc
286305
) async {
287306
emit(state.copyWith(sourcesStatus: ContentManagementStatus.loading));
288307
try {
308+
final isPaginating = event.startAfterId != null;
309+
final previousSources = isPaginating ? state.sources : <Source>[];
310+
289311
final paginatedSources = await _sourcesRepository.readAll(
290312
startAfterId: event.startAfterId,
291313
limit: event.limit,
292314
);
293315
emit(
294316
state.copyWith(
295317
sourcesStatus: ContentManagementStatus.success,
296-
sources: paginatedSources.items,
318+
sources: [...previousSources, ...paginatedSources.items],
297319
sourcesCursor: paginatedSources.cursor,
298320
sourcesHasMore: paginatedSources.hasMore,
299321
),
@@ -323,7 +345,9 @@ class ContentManagementBloc
323345
try {
324346
await _sourcesRepository.create(item: event.source);
325347
// Reload sources after creation
326-
add(const LoadSourcesRequested());
348+
add(
349+
const LoadSourcesRequested(limit: kDefaultRowsPerPage),
350+
);
327351
} on HtHttpException catch (e) {
328352
emit(
329353
state.copyWith(
@@ -349,7 +373,9 @@ class ContentManagementBloc
349373
try {
350374
await _sourcesRepository.update(id: event.id, item: event.source);
351375
// Reload sources after update
352-
add(const LoadSourcesRequested());
376+
add(
377+
const LoadSourcesRequested(limit: kDefaultRowsPerPage),
378+
);
353379
} on HtHttpException catch (e) {
354380
emit(
355381
state.copyWith(
@@ -375,7 +401,9 @@ class ContentManagementBloc
375401
try {
376402
await _sourcesRepository.delete(id: event.id);
377403
// Reload sources after deletion
378-
add(const LoadSourcesRequested());
404+
add(
405+
const LoadSourcesRequested(limit: kDefaultRowsPerPage),
406+
);
379407
} on HtHttpException catch (e) {
380408
emit(
381409
state.copyWith(

lib/content_management/view/categories_page.dart

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dar
66
import 'package:ht_dashboard/l10n/app_localizations.dart';
77
import 'package:ht_dashboard/l10n/l10n.dart';
88
import 'package:ht_dashboard/router/routes.dart';
9+
import 'package:ht_dashboard/shared/constants/pagination_constants.dart';
910
import 'package:ht_dashboard/shared/constants/app_spacing.dart';
1011
import 'package:ht_dashboard/shared/widgets/failure_state_widget.dart';
1112
import 'package:ht_dashboard/shared/widgets/loading_state_widget.dart';
@@ -23,14 +24,12 @@ class CategoriesPage extends StatefulWidget {
2324
}
2425

2526
class _CategoriesPageState extends State<CategoriesPage> {
26-
static const int _rowsPerPage = 10;
27-
2827
@override
2928
void initState() {
3029
super.initState();
3130
context.read<ContentManagementBloc>().add(
32-
const LoadCategoriesRequested(limit: _rowsPerPage),
33-
);
31+
const LoadCategoriesRequested(limit: kDefaultRowsPerPage),
32+
);
3433
}
3534

3635
@override
@@ -53,8 +52,8 @@ class _CategoriesPageState extends State<CategoriesPage> {
5352
return FailureStateWidget(
5453
message: state.errorMessage ?? l10n.unknownError,
5554
onRetry: () => context.read<ContentManagementBloc>().add(
56-
const LoadCategoriesRequested(limit: _rowsPerPage),
57-
),
55+
const LoadCategoriesRequested(limit: kDefaultRowsPerPage),
56+
),
5857
);
5958
}
6059

@@ -82,20 +81,23 @@ class _CategoriesPageState extends State<CategoriesPage> {
8281
source: _CategoriesDataSource(
8382
context: context,
8483
categories: state.categories,
84+
isLoading: state.categoriesStatus == ContentManagementStatus.loading,
85+
hasMore: state.categoriesHasMore,
8586
l10n: l10n,
8687
),
87-
rowsPerPage: _rowsPerPage,
88-
availableRowsPerPage: const [_rowsPerPage],
88+
rowsPerPage: kDefaultRowsPerPage,
89+
availableRowsPerPage: const [kDefaultRowsPerPage],
8990
onPageChanged: (pageIndex) {
90-
final newOffset = pageIndex * _rowsPerPage;
91+
final newOffset = pageIndex * kDefaultRowsPerPage;
9192
if (newOffset >= state.categories.length &&
92-
state.categoriesHasMore) {
93+
state.categoriesHasMore &&
94+
state.categoriesStatus != ContentManagementStatus.loading) {
9395
context.read<ContentManagementBloc>().add(
94-
LoadCategoriesRequested(
95-
startAfterId: state.categoriesCursor,
96-
limit: _rowsPerPage,
97-
),
98-
);
96+
LoadCategoriesRequested(
97+
startAfterId: state.categoriesCursor,
98+
limit: kDefaultRowsPerPage,
99+
),
100+
);
99101
}
100102
},
101103
empty: Center(child: Text(l10n.noCategoriesFound)),
@@ -117,16 +119,27 @@ class _CategoriesDataSource extends DataTableSource {
117119
_CategoriesDataSource({
118120
required this.context,
119121
required this.categories,
122+
required this.isLoading,
123+
required this.hasMore,
120124
required this.l10n,
121125
});
122126

123127
final BuildContext context;
124128
final List<Category> categories;
129+
final bool isLoading;
130+
final bool hasMore;
125131
final AppLocalizations l10n;
126132

127133
@override
128134
DataRow? getRow(int index) {
129135
if (index >= categories.length) {
136+
// This can happen if hasMore is true and the user is on the last page.
137+
// If we are loading, show a spinner. Otherwise, we've reached the end.
138+
if (isLoading) {
139+
return DataRow2(
140+
cells: List.generate(3, (_) => const DataCell(Center(child: CircularProgressIndicator()))),
141+
);
142+
}
130143
return null;
131144
}
132145
final category = categories[index];
@@ -152,8 +165,8 @@ class _CategoriesDataSource extends DataTableSource {
152165
onPressed: () {
153166
// Dispatch delete event
154167
context.read<ContentManagementBloc>().add(
155-
DeleteCategoryRequested(category.id),
156-
);
168+
DeleteCategoryRequested(category.id),
169+
);
157170
},
158171
),
159172
],
@@ -164,10 +177,20 @@ class _CategoriesDataSource extends DataTableSource {
164177
}
165178

166179
@override
167-
bool get isRowCountApproximate => false;
180+
bool get isRowCountApproximate => hasMore;
168181

169182
@override
170-
int get rowCount => categories.length;
183+
int get rowCount {
184+
// If we have more items to fetch, we add 1 to the current length.
185+
// This signals to PaginatedDataTable2 that there is at least one more page,
186+
// which enables the 'next page' button.
187+
if (hasMore) {
188+
// When loading, we show an extra row for the spinner.
189+
// Otherwise, we just indicate that there are more rows.
190+
return isLoading ? categories.length + 1 : categories.length + kDefaultRowsPerPage;
191+
}
192+
return categories.length;
193+
}
171194

172195
@override
173196
int get selectedRowCount => 0;

lib/content_management/view/create_category_page.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
44
import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart';
55
import 'package:ht_dashboard/content_management/bloc/create_category/create_category_bloc.dart';
66
import 'package:ht_dashboard/l10n/l10n.dart';
7+
import 'package:ht_dashboard/shared/constants/pagination_constants.dart';
78
import 'package:ht_dashboard/shared/shared.dart';
89
import 'package:ht_data_repository/ht_data_repository.dart';
910
import 'package:ht_shared/ht_shared.dart';
@@ -82,7 +83,9 @@ class _CreateCategoryViewState extends State<_CreateCategoryView> {
8283
),
8384
);
8485
context.read<ContentManagementBloc>().add(
85-
const LoadCategoriesRequested(),
86+
const LoadCategoriesRequested(
87+
limit: kDefaultRowsPerPage,
88+
),
8689
);
8790
context.pop();
8891
}

lib/content_management/view/create_headline_page.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
44
import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart';
55
import 'package:ht_dashboard/content_management/bloc/create_headline/create_headline_bloc.dart';
66
import 'package:ht_dashboard/l10n/l10n.dart';
7+
import 'package:ht_dashboard/shared/constants/pagination_constants.dart';
78
import 'package:ht_dashboard/shared/shared.dart';
89
import 'package:ht_data_repository/ht_data_repository.dart';
910
import 'package:ht_shared/ht_shared.dart';
@@ -84,7 +85,9 @@ class _CreateHeadlineViewState extends State<_CreateHeadlineView> {
8485
),
8586
);
8687
context.read<ContentManagementBloc>().add(
87-
const LoadHeadlinesRequested(),
88+
const LoadHeadlinesRequested(
89+
limit: kDefaultRowsPerPage,
90+
),
8891
);
8992
context.pop();
9093
}

lib/content_management/view/create_source_page.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dar
55
import 'package:ht_dashboard/content_management/bloc/create_source/create_source_bloc.dart';
66
import 'package:ht_dashboard/content_management/bloc/edit_source/edit_source_bloc.dart';
77
import 'package:ht_dashboard/l10n/l10n.dart';
8+
import 'package:ht_dashboard/shared/constants/pagination_constants.dart';
89
import 'package:ht_dashboard/shared/shared.dart';
910
import 'package:ht_data_repository/ht_data_repository.dart';
1011
import 'package:ht_shared/ht_shared.dart';
@@ -82,7 +83,9 @@ class _CreateSourceViewState extends State<_CreateSourceView> {
8283
SnackBar(content: Text(l10n.sourceCreatedSuccessfully)),
8384
);
8485
context.read<ContentManagementBloc>().add(
85-
const LoadSourcesRequested(),
86+
const LoadSourcesRequested(
87+
limit: kDefaultRowsPerPage,
88+
),
8689
);
8790
context.pop();
8891
}

lib/content_management/view/edit_category_page.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
44
import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart';
55
import 'package:ht_dashboard/content_management/bloc/edit_category/edit_category_bloc.dart';
66
import 'package:ht_dashboard/l10n/l10n.dart';
7+
import 'package:ht_dashboard/shared/constants/pagination_constants.dart';
78
import 'package:ht_dashboard/shared/shared.dart';
89
import 'package:ht_data_repository/ht_data_repository.dart';
910
import 'package:ht_shared/ht_shared.dart';
@@ -108,7 +109,9 @@ class _EditCategoryViewState extends State<_EditCategoryView> {
108109
const SnackBar(content: Text('Category updated successfully.')),
109110
);
110111
context.read<ContentManagementBloc>().add(
111-
const LoadCategoriesRequested(),
112+
const LoadCategoriesRequested(
113+
limit: kDefaultRowsPerPage,
114+
),
112115
);
113116
context.pop();
114117
}

lib/content_management/view/edit_headline_page.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
44
import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart';
55
import 'package:ht_dashboard/content_management/bloc/edit_headline/edit_headline_bloc.dart';
66
import 'package:ht_dashboard/l10n/l10n.dart';
7+
import 'package:ht_dashboard/shared/constants/pagination_constants.dart';
78
import 'package:ht_dashboard/shared/shared.dart';
89
import 'package:ht_data_repository/ht_data_repository.dart';
910
import 'package:ht_shared/ht_shared.dart';
@@ -114,7 +115,9 @@ class _EditHeadlineViewState extends State<_EditHeadlineView> {
114115
),
115116
);
116117
context.read<ContentManagementBloc>().add(
117-
const LoadHeadlinesRequested(),
118+
const LoadHeadlinesRequested(
119+
limit: kDefaultRowsPerPage,
120+
),
118121
);
119122
context.pop();
120123
}

0 commit comments

Comments
 (0)