Skip to content

Commit 476bea6

Browse files
committed
refactor: Migrate all NeonListView usage to ResultListBuilder
Signed-off-by: provokateurin <kate@provokateurin.de>
1 parent 84656d6 commit 476bea6

File tree

10 files changed

+203
-229
lines changed

10 files changed

+203
-229
lines changed

packages/neon/neon_dashboard/lib/src/blocs/dashboard.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class _DashboardBloc extends InteractiveBloc implements DashboardBloc {
3131
itemsV2.listen((_) => _updateItems());
3232

3333
widgets.listen((result) async {
34-
if (!result.hasData) {
34+
if (!result.hasSuccessfulData) {
3535
return;
3636
}
3737

packages/neon/neon_dashboard/lib/src/pages/main.dart

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ class DashboardMainPage extends StatelessWidget {
3232
final weatherStatusBloc = accountsBloc.activeWeatherStatusBloc;
3333

3434
return NeonCustomBackground(
35-
child: ResultBuilder.behaviorSubject(
35+
child: ResultListBuilder(
36+
scrollKey: 'dashboard',
37+
onRefresh: bloc.refresh,
3638
subject: bloc.widgets,
37-
builder: (context, widgetsResult) => ResultBuilder.behaviorSubject(
39+
builder: (context, widgets) => ResultBuilder.behaviorSubject(
3840
subject: bloc.items,
3941
builder: (context, itemsResult) {
4042
final children = <Widget>[
@@ -46,9 +48,9 @@ class DashboardMainPage extends StatelessWidget {
4648
];
4749

4850
var minHeight = 504.0;
49-
if (widgetsResult.hasData) {
50-
final widgets = <Widget>[];
51-
for (final widget in widgetsResult.requireData) {
51+
if (widgets.isNotEmpty) {
52+
final subChildren = <Widget>[];
53+
for (final widget in widgets) {
5254
final items = buildWidgetItems(
5355
context: context,
5456
widget: widget,
@@ -58,7 +60,7 @@ class DashboardMainPage extends StatelessWidget {
5860
final height = items.map((i) => i.height!).reduce((a, b) => a + b);
5961
minHeight = max(minHeight, height);
6062

61-
widgets.add(
63+
subChildren.add(
6264
DashboardWidget(
6365
widget: widget,
6466
children: items,
@@ -71,7 +73,7 @@ class DashboardMainPage extends StatelessWidget {
7173
alignment: WrapAlignment.center,
7274
spacing: 8,
7375
runSpacing: 8,
74-
children: widgets
76+
children: subChildren
7577
.map(
7678
(widget) => SizedBox(
7779
width: 320,
@@ -90,26 +92,17 @@ class DashboardMainPage extends StatelessWidget {
9092
);
9193
}
9294

93-
return Center(
94-
child: NeonListView.custom(
95-
scrollKey: 'dashboard',
96-
isLoading: widgetsResult.isLoading || itemsResult.isLoading,
97-
error: widgetsResult.error ?? itemsResult.error,
98-
onRefresh: bloc.refresh,
99-
sliver: SliverFillRemaining(
100-
hasScrollBody: false,
101-
child: Column(
102-
mainAxisAlignment: MainAxisAlignment.center,
103-
mainAxisSize: MainAxisSize.min,
104-
children: children
105-
.intersperse(
106-
const SizedBox(
107-
height: 40,
108-
),
109-
)
110-
.toList(),
111-
),
112-
),
95+
return SliverFillRemaining(
96+
hasScrollBody: false,
97+
child: Column(
98+
mainAxisAlignment: MainAxisAlignment.center,
99+
children: children
100+
.intersperse(
101+
const SizedBox(
102+
height: 40,
103+
),
104+
)
105+
.toList(),
113106
),
114107
);
115108
},

packages/neon/neon_files/lib/src/widgets/browser_view.dart

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -39,42 +39,52 @@ class _FilesBrowserViewState extends State<FilesBrowserView> {
3939
}
4040

4141
@override
42-
Widget build(BuildContext context) => ResultBuilder.behaviorSubject(
43-
subject: widget.bloc.files,
44-
builder: (context, filesSnapshot) => StreamBuilder(
45-
stream: widget.bloc.uri,
46-
builder: (context, uriSnapshot) => StreamBuilder(
47-
stream: widget.filesBloc.tasks,
48-
builder: (context, tasksSnapshot) {
49-
if (!uriSnapshot.hasData || !tasksSnapshot.hasData) {
50-
return const SizedBox();
42+
Widget build(BuildContext context) => StreamBuilder(
43+
stream: widget.bloc.uri.distinct(),
44+
builder: (context, uriSnapshot) {
45+
if (!uriSnapshot.hasData) {
46+
return const SizedBox.shrink();
47+
}
48+
return BackButtonListener(
49+
onBackButtonPressed: () async {
50+
final parent = uriSnapshot.requireData.parent;
51+
if (parent != null) {
52+
widget.bloc.setPath(parent);
53+
return true;
5154
}
52-
return BackButtonListener(
53-
onBackButtonPressed: () async {
54-
final parent = uriSnapshot.requireData.parent;
55-
if (parent != null) {
56-
widget.bloc.setPath(parent);
57-
return true;
58-
}
59-
return false;
55+
return false;
56+
},
57+
child: ResultListBuilder(
58+
subject: widget.bloc.files,
59+
scrollKey: 'files-${uriSnapshot.requireData.path}',
60+
onRefresh: widget.bloc.refresh,
61+
topScrollingChildren: [
62+
FilesBrowserNavigator(
63+
uri: uriSnapshot.requireData,
64+
bloc: widget.bloc,
65+
),
66+
],
67+
builder: (context, files) => SortBoxBuilder(
68+
sortBox: filesSortBox,
69+
sortProperty: widget.bloc.options.filesSortPropertyOption,
70+
sortBoxOrder: widget.bloc.options.filesSortBoxOrderOption,
71+
presort: const {
72+
(property: FilesSortProperty.isFolder, order: SortBoxOrder.ascending),
6073
},
61-
child: SortBoxBuilder(
62-
sortBox: filesSortBox,
63-
sortProperty: widget.bloc.options.filesSortPropertyOption,
64-
sortBoxOrder: widget.bloc.options.filesSortBoxOrderOption,
65-
presort: const {
66-
(property: FilesSortProperty.isFolder, order: SortBoxOrder.ascending),
67-
},
68-
input: filesSnapshot.data?.sublist(1).toList(),
69-
builder: (context, sorted) {
70-
final uploadingTaskTiles = buildUploadTasks(tasksSnapshot.requireData, sorted);
74+
input: files.isNotEmpty ? files.sublist(1).toList() : <WebDavFile>[],
75+
builder: (context, sorted) => StreamBuilder(
76+
stream: widget.filesBloc.tasks,
77+
builder: (context, tasksSnapshot) {
78+
final uploadingTaskTiles = buildUploadTasks(tasksSnapshot.data ?? BuiltList(), sorted).toList();
7179

72-
return NeonListView(
73-
scrollKey: 'files-${uriSnapshot.requireData.path}',
74-
itemCount: sorted.length,
80+
return SliverList.builder(
81+
itemCount: uploadingTaskTiles.length + sorted.length,
7582
itemBuilder: (context, index) {
76-
final file = sorted[index];
77-
final matchingTask = tasksSnapshot.requireData.firstWhereOrNull(
83+
if (index < uploadingTaskTiles.length) {
84+
return uploadingTaskTiles[index];
85+
}
86+
final file = sorted[index - uploadingTaskTiles.length];
87+
final matchingTask = tasksSnapshot.data?.firstWhereOrNull(
7888
(task) => file.name == task.uri.name && widget.bloc.uri.value == task.uri.parent,
7989
);
8090

@@ -93,23 +103,13 @@ class _FilesBrowserViewState extends State<FilesBrowserView> {
93103
details: details,
94104
);
95105
},
96-
isLoading: filesSnapshot.isLoading,
97-
error: filesSnapshot.error,
98-
onRefresh: widget.bloc.refresh,
99-
topScrollingChildren: [
100-
FilesBrowserNavigator(
101-
uri: uriSnapshot.requireData,
102-
bloc: widget.bloc,
103-
),
104-
...uploadingTaskTiles,
105-
],
106106
);
107107
},
108108
),
109-
);
110-
},
111-
),
112-
),
109+
),
110+
),
111+
);
112+
},
113113
);
114114

115115
Iterable<Widget> buildUploadTasks(BuiltList<FilesTask> tasks, List<WebDavFile> files) sync* {

packages/neon/neon_news/lib/src/widgets/articles_view.dart

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -43,75 +43,73 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
4343
}
4444

4545
@override
46-
Widget build(BuildContext context) => ResultBuilder.behaviorSubject(
47-
subject: widget.newsBloc.feeds,
48-
builder: (context, feeds) => ResultBuilder.behaviorSubject(
46+
Widget build(BuildContext context) => StreamBuilder(
47+
stream: widget.newsBloc.feeds.map((event) => event.data).distinct(),
48+
builder: (context, feeds) => ResultListBuilder(
4949
subject: widget.bloc.articles,
50-
builder: (context, articles) => SortBoxBuilder(
50+
scrollKey: 'news-articles',
51+
onRefresh: () async {
52+
await Future.wait([
53+
widget.bloc.refresh(),
54+
widget.newsBloc.refresh(),
55+
]);
56+
},
57+
topFixedChildren: [
58+
StreamBuilder(
59+
stream: widget.bloc.filterType,
60+
builder: (context, selectedFilterTypeSnapshot) => Container(
61+
margin: const EdgeInsets.symmetric(horizontal: 15),
62+
child: DropdownButton(
63+
isExpanded: true,
64+
value: selectedFilterTypeSnapshot.data,
65+
items: [
66+
FilterType.all,
67+
FilterType.unread,
68+
if (widget.bloc.listType == null) ...[
69+
FilterType.starred,
70+
],
71+
].map(
72+
(a) {
73+
late final String label;
74+
switch (a) {
75+
case FilterType.all:
76+
label = NewsLocalizations.of(context).articlesFilterAll;
77+
case FilterType.unread:
78+
label = NewsLocalizations.of(context).articlesFilterUnread;
79+
case FilterType.starred:
80+
label = NewsLocalizations.of(context).articlesFilterStarred;
81+
default:
82+
throw Exception('FilterType $a should not be shown');
83+
}
84+
return DropdownMenuItem(
85+
value: a,
86+
child: Text(label),
87+
);
88+
},
89+
).toList(),
90+
onChanged: (value) {
91+
widget.bloc.setFilterType(value!);
92+
},
93+
),
94+
),
95+
),
96+
],
97+
builder: (context, data) => SortBoxBuilder(
5198
sortBox: articlesSortBox,
5299
sortProperty: widget.newsBloc.options.articlesSortPropertyOption,
53100
sortBoxOrder: widget.newsBloc.options.articlesSortBoxOrderOption,
54-
input: articles.data?.toList(),
55-
builder: (context, sorted) => NeonListView(
56-
scrollKey: 'news-articles',
57-
isLoading: articles.isLoading || feeds.isLoading,
58-
error: articles.error ?? feeds.error,
59-
onRefresh: () async {
60-
await Future.wait([
61-
widget.bloc.refresh(),
62-
widget.newsBloc.refresh(),
63-
]);
64-
},
101+
input: data.toList(),
102+
builder: (context, sorted) => SliverList.builder(
65103
itemCount: sorted.length,
66104
itemBuilder: (context, index) {
67105
final article = sorted[index];
68106

69107
return _buildArticle(
70108
context,
71109
article,
72-
feeds.requireData.singleWhere((feed) => feed.id == article.feedId),
110+
feeds.data?.singleWhere((feed) => feed.id == article.feedId),
73111
);
74112
},
75-
topFixedChildren: [
76-
StreamBuilder(
77-
stream: widget.bloc.filterType,
78-
builder: (context, selectedFilterTypeSnapshot) => Container(
79-
margin: const EdgeInsets.symmetric(horizontal: 15),
80-
child: DropdownButton(
81-
isExpanded: true,
82-
value: selectedFilterTypeSnapshot.data,
83-
items: [
84-
FilterType.all,
85-
FilterType.unread,
86-
if (widget.bloc.listType == null) ...[
87-
FilterType.starred,
88-
],
89-
].map(
90-
(a) {
91-
late final String label;
92-
switch (a) {
93-
case FilterType.all:
94-
label = NewsLocalizations.of(context).articlesFilterAll;
95-
case FilterType.unread:
96-
label = NewsLocalizations.of(context).articlesFilterUnread;
97-
case FilterType.starred:
98-
label = NewsLocalizations.of(context).articlesFilterStarred;
99-
default:
100-
throw Exception('FilterType $a should not be shown');
101-
}
102-
return DropdownMenuItem(
103-
value: a,
104-
child: Text(label),
105-
);
106-
},
107-
).toList(),
108-
onChanged: (value) {
109-
widget.bloc.setFilterType(value!);
110-
},
111-
),
112-
),
113-
),
114-
],
115113
),
116114
),
117115
),
@@ -120,7 +118,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
120118
Widget _buildArticle(
121119
BuildContext context,
122120
news.Article article,
123-
news.Feed feed,
121+
news.Feed? feed,
124122
) =>
125123
ListTile(
126124
title: Row(
@@ -151,11 +149,15 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
151149
bottom: 8,
152150
right: 8,
153151
),
154-
child: NewsFeedIcon(
155-
feed: feed,
156-
size: smallIconSize,
157-
borderRadius: const BorderRadius.all(Radius.circular(2)),
158-
),
152+
child: feed != null
153+
? NewsFeedIcon(
154+
feed: feed,
155+
size: smallIconSize,
156+
borderRadius: const BorderRadius.all(Radius.circular(2)),
157+
)
158+
: const SizedBox.square(
159+
dimension: smallIconSize,
160+
),
159161
),
160162
RelativeTime(
161163
date: DateTime.fromMillisecondsSinceEpoch(article.pubDate * 1000),
@@ -167,13 +169,14 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
167169
const SizedBox(
168170
width: 5,
169171
),
170-
Flexible(
171-
child: Text(
172-
feed.title,
173-
maxLines: 1,
174-
overflow: TextOverflow.ellipsis,
172+
if (feed != null)
173+
Flexible(
174+
child: Text(
175+
feed.title,
176+
maxLines: 1,
177+
overflow: TextOverflow.ellipsis,
178+
),
175179
),
176-
),
177180
],
178181
),
179182
trailing: IconButton(

0 commit comments

Comments
 (0)