Skip to content

Commit 2a4a70b

Browse files
committed
feat(content-management): enhance pagination handling with loading state indicators for categories, headlines, and sources
1 parent fa42e40 commit 2a4a70b

File tree

3 files changed

+62
-29
lines changed

3 files changed

+62
-29
lines changed

lib/content_management/view/categories_page.dart

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class _CategoriesPageState extends State<CategoriesPage> {
8181
source: _CategoriesDataSource(
8282
context: context,
8383
categories: state.categories,
84+
isLoading: state.categoriesStatus == ContentManagementStatus.loading,
8485
hasMore: state.categoriesHasMore,
8586
l10n: l10n,
8687
),
@@ -89,7 +90,8 @@ class _CategoriesPageState extends State<CategoriesPage> {
8990
onPageChanged: (pageIndex) {
9091
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(
9496
LoadCategoriesRequested(
9597
startAfterId: state.categoriesCursor,
@@ -117,22 +119,27 @@ class _CategoriesDataSource extends DataTableSource {
117119
_CategoriesDataSource({
118120
required this.context,
119121
required this.categories,
122+
required this.isLoading,
120123
required this.hasMore,
121124
required this.l10n,
122125
});
123126

124127
final BuildContext context;
125128
final List<Category> categories;
129+
final bool isLoading;
126130
final bool hasMore;
127131
final AppLocalizations l10n;
128132

129133
@override
130134
DataRow? getRow(int index) {
131135
if (index >= categories.length) {
132136
// This can happen if hasMore is true and the user is on the last page.
133-
// The table will try to build one extra row that is out of bounds.
134-
// We return null to signify the end of the available data.
135-
// The onPageChanged callback will handle fetching more data.
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+
}
136143
return null;
137144
}
138145
final category = categories[index];
@@ -170,15 +177,17 @@ class _CategoriesDataSource extends DataTableSource {
170177
}
171178

172179
@override
173-
bool get isRowCountApproximate => true;
180+
bool get isRowCountApproximate => hasMore;
174181

175182
@override
176183
int get rowCount {
177184
// If we have more items to fetch, we add 1 to the current length.
178185
// This signals to PaginatedDataTable2 that there is at least one more page,
179186
// which enables the 'next page' button.
180187
if (hasMore) {
181-
return categories.length + 1;
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;
182191
}
183192
return categories.length;
184193
}

lib/content_management/view/headlines_page.dart

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class _HeadlinesPageState extends State<HeadlinesPage> {
8686
source: _HeadlinesDataSource(
8787
context: context,
8888
headlines: state.headlines,
89+
isLoading: state.headlinesStatus == ContentManagementStatus.loading,
8990
hasMore: state.headlinesHasMore,
9091
l10n: l10n,
9192
),
@@ -94,7 +95,8 @@ class _HeadlinesPageState extends State<HeadlinesPage> {
9495
onPageChanged: (pageIndex) {
9596
final newOffset = pageIndex * kDefaultRowsPerPage;
9697
if (newOffset >= state.headlines.length &&
97-
state.headlinesHasMore) {
98+
state.headlinesHasMore &&
99+
state.headlinesStatus != ContentManagementStatus.loading) {
98100
context.read<ContentManagementBloc>().add(
99101
LoadHeadlinesRequested(
100102
startAfterId: state.headlinesCursor,
@@ -122,22 +124,27 @@ class _HeadlinesDataSource extends DataTableSource {
122124
_HeadlinesDataSource({
123125
required this.context,
124126
required this.headlines,
127+
required this.isLoading,
125128
required this.hasMore,
126129
required this.l10n,
127130
});
128131

129132
final BuildContext context;
130133
final List<Headline> headlines;
134+
final bool isLoading;
131135
final bool hasMore;
132136
final AppLocalizations l10n;
133137

134138
@override
135139
DataRow? getRow(int index) {
136140
if (index >= headlines.length) {
137141
// This can happen if hasMore is true and the user is on the last page.
138-
// The table will try to build one extra row that is out of bounds.
139-
// We return null to signify the end of the available data.
140-
// The onPageChanged callback will handle fetching more data.
142+
// If we are loading, show a spinner. Otherwise, we've reached the end.
143+
if (isLoading) {
144+
return DataRow2(
145+
cells: List.generate(4, (_) => const DataCell(Center(child: CircularProgressIndicator()))),
146+
);
147+
}
141148
return null;
142149
}
143150
final headline = headlines[index];
@@ -182,15 +189,17 @@ class _HeadlinesDataSource extends DataTableSource {
182189
}
183190

184191
@override
185-
bool get isRowCountApproximate => true;
192+
bool get isRowCountApproximate => hasMore;
186193

187194
@override
188195
int get rowCount {
189196
// If we have more items to fetch, we add 1 to the current length.
190197
// This signals to PaginatedDataTable2 that there is at least one more page,
191198
// which enables the 'next page' button.
192199
if (hasMore) {
193-
return headlines.length + 1;
200+
// When loading, we show an extra row for the spinner.
201+
// Otherwise, we just indicate that there are more rows.
202+
return isLoading ? headlines.length + 1 : headlines.length + kDefaultRowsPerPage;
194203
}
195204
return headlines.length;
196205
}

lib/content_management/view/sources_page.dart

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ class _SourcesPageState extends State<SourcesPage> {
2828
void initState() {
2929
super.initState();
3030
context.read<ContentManagementBloc>().add(
31-
const LoadSourcesRequested(limit: kDefaultRowsPerPage),
32-
);
31+
const LoadSourcesRequested(limit: kDefaultRowsPerPage),
32+
);
3333
}
3434

3535
@override
@@ -52,8 +52,8 @@ class _SourcesPageState extends State<SourcesPage> {
5252
return FailureStateWidget(
5353
message: state.errorMessage ?? l10n.unknownError,
5454
onRetry: () => context.read<ContentManagementBloc>().add(
55-
const LoadSourcesRequested(limit: kDefaultRowsPerPage),
56-
),
55+
const LoadSourcesRequested(limit: kDefaultRowsPerPage),
56+
),
5757
);
5858
}
5959

@@ -85,20 +85,23 @@ class _SourcesPageState extends State<SourcesPage> {
8585
source: _SourcesDataSource(
8686
context: context,
8787
sources: state.sources,
88+
isLoading: state.sourcesStatus == ContentManagementStatus.loading,
8889
hasMore: state.sourcesHasMore,
8990
l10n: l10n,
9091
),
9192
rowsPerPage: kDefaultRowsPerPage,
9293
availableRowsPerPage: const [kDefaultRowsPerPage],
9394
onPageChanged: (pageIndex) {
9495
final newOffset = pageIndex * kDefaultRowsPerPage;
95-
if (newOffset >= state.sources.length && state.sourcesHasMore) {
96+
if (newOffset >= state.sources.length &&
97+
state.sourcesHasMore &&
98+
state.sourcesStatus != ContentManagementStatus.loading) {
9699
context.read<ContentManagementBloc>().add(
97-
LoadSourcesRequested(
98-
startAfterId: state.sourcesCursor,
99-
limit: kDefaultRowsPerPage,
100-
),
101-
);
100+
LoadSourcesRequested(
101+
startAfterId: state.sourcesCursor,
102+
limit: kDefaultRowsPerPage,
103+
),
104+
);
102105
}
103106
},
104107
empty: Center(child: Text(l10n.noSourcesFound)),
@@ -120,22 +123,30 @@ class _SourcesDataSource extends DataTableSource {
120123
_SourcesDataSource({
121124
required this.context,
122125
required this.sources,
126+
required this.isLoading,
123127
required this.hasMore,
124128
required this.l10n,
125129
});
126130

127131
final BuildContext context;
128132
final List<Source> sources;
133+
final bool isLoading;
129134
final bool hasMore;
130135
final AppLocalizations l10n;
131136

132137
@override
133138
DataRow? getRow(int index) {
134139
if (index >= sources.length) {
135140
// This can happen if hasMore is true and the user is on the last page.
136-
// The table will try to build one extra row that is out of bounds.
137-
// We return null to signify the end of the available data.
138-
// The onPageChanged callback will handle fetching more data.
141+
// If we are loading, show a spinner. Otherwise, we've reached the end.
142+
if (isLoading) {
143+
return DataRow2(
144+
cells: List.generate(
145+
4,
146+
(_) => const DataCell(Center(child: CircularProgressIndicator())),
147+
),
148+
);
149+
}
139150
return null;
140151
}
141152
final source = sources[index];
@@ -162,8 +173,8 @@ class _SourcesDataSource extends DataTableSource {
162173
onPressed: () {
163174
// Dispatch delete event
164175
context.read<ContentManagementBloc>().add(
165-
DeleteSourceRequested(source.id),
166-
);
176+
DeleteSourceRequested(source.id),
177+
);
167178
},
168179
),
169180
],
@@ -174,15 +185,19 @@ class _SourcesDataSource extends DataTableSource {
174185
}
175186

176187
@override
177-
bool get isRowCountApproximate => true;
188+
bool get isRowCountApproximate => hasMore;
178189

179190
@override
180191
int get rowCount {
181192
// If we have more items to fetch, we add 1 to the current length.
182193
// This signals to PaginatedDataTable2 that there is at least one more page,
183194
// which enables the 'next page' button.
184195
if (hasMore) {
185-
return sources.length + 1;
196+
// When loading, we show an extra row for the spinner.
197+
// Otherwise, we just indicate that there are more rows.
198+
return isLoading
199+
? sources.length + 1
200+
: sources.length + kDefaultRowsPerPage;
186201
}
187202
return sources.length;
188203
}

0 commit comments

Comments
 (0)