Skip to content

Commit 6e07764

Browse files
author
tensor-programming
committed
refactor, comment and cleanup
1 parent a90ffe1 commit 6e07764

15 files changed

+247
-162
lines changed

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
1+
## 0.1.0
2+
3+
- Large refactor to add comments and reorganize the code
4+
- Make info and release streams into reusable Observables
5+
- [Bugs] clean up random minor bugs.
6+
7+
## 0.0.9
8+
9+
- minor updates to UI
10+
- [Bug] further smoothed out the filtering process
11+
- Small Refactors
12+
13+
## 0.0.8
14+
15+
- Fixed a bunch of bugs
16+
- Cleaned up the filtering functionality
17+
- Moved update to a button in the drawer to give users more control
18+
- Major refactor
19+
20+
## 0.0.7
21+
22+
- Added Category Filter
23+
- [Bug] Fixed when category has "-task" or "task-" appended to it
24+
- cleaned up github API call
25+
- Made notifier only look for new releases on launch
26+
27+
## 0.0.5
28+
29+
- Added the Vote CountDown timer
30+
- Added the Notification for Application Updating
31+
- Added the Vote Power
32+
- [Bug] Fixed Pkg Information not showing after you shut the drawer
33+
34+
## 0.0.3
35+
36+
- Added information Drawer with instructions and thanks
37+
- Refactored for more BloCs
38+
- Added a second BLoC which provides package_info and connectivity information
39+
140
## 0.0.2
241

342
- Removed Share in favor of url_launcher.

lib/blocs/contribution_bloc.dart

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
11
import 'dart:async';
2+
import 'package:rxdart/rxdart.dart';
3+
24
import 'package:utopian_rocks/model/model.dart';
35
import 'package:utopian_rocks/model/repository.dart';
4-
import 'package:utopian_rocks/model/htmlParser.dart';
6+
import 'package:utopian_rocks/model/html_parser.dart';
57
import 'package:utopian_rocks/utils/utils.dart';
6-
import 'package:rxdart/rxdart.dart';
78

89
// Contribution buisness logic class
910
class ContributionBloc {
1011
final Api api;
1112
final ParseWebsite parseWebsite;
1213

13-
// setup an empty stream for the results
14+
// setup an empty streams for results, filteredResults, voteCount and timer.
1415
Stream<List<Contribution>> _results = Stream.empty();
1516
Observable<List<Contribution>> _filteredResults = Observable.empty();
1617
Stream<String> _voteCount = Stream.empty();
1718
Stream<int> _timer = Stream.empty();
18-
// BehaviorSubject allows seeding the tabIndex data to pending.
19+
20+
// BehaviorSubject allows seeding the tabIndex data to unreviewed.
1921
// Tagname is the name of the tab and modifier for the contributions
2022
BehaviorSubject<String> _tabName =
2123
BehaviorSubject<String>(seedValue: 'unreviewed');
22-
// Stream<String> _log = Stream.empty();
23-
24+
// BehaviorSubject for the filter, seeded to 'all' by default.
2425
BehaviorSubject<String> _filter = BehaviorSubject<String>(seedValue: 'all');
2526

27+
// getters for the streams.
2628
Stream<List<Contribution>> get results => _results;
27-
Observable<List<Contribution>> get filteredResults => _filteredResults;
29+
Stream<List<Contribution>> get filteredResults => _filteredResults;
2830
Stream<String> get voteCount => _voteCount;
2931
Stream<int> get timer => _timer;
3032

33+
// getters for the sinks.
3134
Sink<String> get tabName => _tabName;
3235
Sink<String> get filter => _filter;
3336

3437
ContributionBloc(this.api, this.parseWebsite) {
38+
// get results by putting a new tabname into the tabname [Sink]
3539
_results = _tabName
36-
3740
// Apply the api updateContributions function to tabIndex stream to get results.
3841
.asyncMap(api.updateContributions)
3942
.asBroadcastStream();
40-
43+
// Combine the Filter Sink and the results stream with the ApplyFilter to create a filtered list of Contributions.
4144
_filteredResults = Observable.combineLatest2(_filter, _results, applyFilter)
4245
.asBroadcastStream();
4346

44-
_voteCount = Observable.fromFuture(
45-
parseWebsite.getVotePower(),
46-
).asBroadcastStream();
47+
// put vote count into observable and feed it to the UI.
48+
_voteCount =
49+
Observable.fromFuture(parseWebsite.getVotePower()).asBroadcastStream();
4750

51+
// increment timer by emitting stream every second.
4852
_timer = Observable.periodic(Duration(seconds: 1), (x) => x)
4953
.asyncMap(parseWebsite.getTimer)
5054
.asBroadcastStream();

lib/blocs/information_bloc.dart

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'dart:async';
33
import 'package:rxdart/rxdart.dart';
44
import 'package:package_info/package_info.dart';
55

6-
import 'package:utopian_rocks/model/githubApi.dart';
6+
import 'package:utopian_rocks/model/github_api.dart';
77
import 'package:utopian_rocks/model/model.dart';
88

99
class InformationBloc {
@@ -20,9 +20,18 @@ class InformationBloc {
2020
// BLoc that serves package information to the information drawer.
2121
// Bloc gets Github release information only on application start.
2222
InformationBloc(this.packageInfo, this.api) {
23-
_infoStream = Observable.fromFuture(packageInfo).asBroadcastStream();
24-
// release information is served as a normal stream which can only be subscribed to once.
23+
// Serve the [PackageInformation] using a defer observable which can only be subscribed to once.
24+
// This defer observable is re-created when the drawer opens.
25+
_infoStream = Observable.defer(
26+
() => Observable.fromFuture(packageInfo).asBroadcastStream(),
27+
reusable: true,
28+
);
29+
// release information is served as a defered Observable which can only be subscribed to once.
2530
// This stream also only has one element in it. This is done to stop from overflowing the Github API.
26-
_releases = Observable.fromFuture(api.getReleases()).asBroadcastStream();
31+
// This defer observable is re-created on every button press.
32+
_releases = Observable.defer(
33+
() => Observable.fromFuture(api.getReleases()).asBroadcastStream(),
34+
reusable: true,
35+
);
2736
}
2837
}

lib/components/bottom_sheet.dart

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import 'package:flutter/material.dart';
2+
3+
import 'package:intl/intl.dart';
4+
5+
import 'package:utopian_rocks/blocs/contribution_bloc.dart';
6+
import 'package:utopian_rocks/utils/utils.dart';
7+
8+
class BottomSheetbar extends StatelessWidget {
9+
final ContributionBloc contributionBloc;
10+
11+
BottomSheetbar(this.contributionBloc);
12+
13+
@override
14+
Widget build(BuildContext context) {
15+
// Build the [BottomAppBar] based on the voteCount stream
16+
return StreamBuilder(
17+
stream: contributionBloc.voteCount,
18+
builder: (context, votecountSnapshot) {
19+
return BottomAppBar(
20+
color: Color(0xff26A69A),
21+
child: Row(
22+
children: [
23+
// Build the first [Text] based on the timer stream
24+
StreamBuilder(
25+
stream: contributionBloc.timer,
26+
builder: (context, timerSnapshot) {
27+
// use intl to parse the amount of seconds with [DateTime] into a [DateFormat]
28+
return Text(
29+
'Next Vote Cycle: ${DateFormat.Hms().format(DateTime(0, 0, 0, 0, 0, timerSnapshot.data ?? 0))}',
30+
style: TextStyle(fontWeight: FontWeight.w700),
31+
);
32+
}),
33+
// Parse the votecount as a double with 4 digits
34+
Text(
35+
'Vote Power: ${double.parse(votecountSnapshot.data ?? '0.0').toStringAsPrecision(4)}',
36+
style: TextStyle(fontWeight: FontWeight.w700),
37+
),
38+
Padding(
39+
padding: EdgeInsets.only(left: 12.0),
40+
// Create Filter menu on bottom right
41+
child: _generateMenu(categories, contributionBloc),
42+
),
43+
],
44+
mainAxisAlignment: MainAxisAlignment.spaceAround,
45+
crossAxisAlignment: CrossAxisAlignment.center,
46+
));
47+
});
48+
}
49+
50+
Widget _generateMenu(
51+
List<String> categories,
52+
ContributionBloc bloc,
53+
) {
54+
return PopupMenuButton<String>(
55+
tooltip: 'Filter Contribution Categories',
56+
// When fitler selected add it to the filter [Sink]
57+
onSelected: (category) => bloc.filter.add(category),
58+
// Map through all of the categories and then programmatically create the menu items.
59+
itemBuilder: (context) => categories
60+
.map((cate) => PopupMenuItem(
61+
height: 40.0,
62+
value: cate,
63+
child: ListTile(
64+
leading: Icon(
65+
IconData(
66+
icons[cate],
67+
fontFamily: 'Utopicons',
68+
),
69+
color: colors[cate],
70+
),
71+
title: Text(cate),
72+
),
73+
))
74+
.toList(),
75+
);
76+
}
77+
}

lib/components/drawer.dart

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import 'package:flutter/material.dart';
2-
import 'package:utopian_rocks/providers/information_provider.dart';
32

4-
import 'package:url_launcher/url_launcher.dart';
3+
import 'package:utopian_rocks/utils/utils.dart';
4+
5+
import 'package:utopian_rocks/providers/information_provider.dart';
56

67
class InformationDrawer extends StatelessWidget {
78
final AsyncSnapshot infoStreamSnapshot;
@@ -10,37 +11,23 @@ class InformationDrawer extends StatelessWidget {
1011

1112
@override
1213
Widget build(BuildContext context) {
14+
// Create the informatino drawer based on the [infoStreamSnapshot]
1315
return Drawer(
1416
semanticLabel: 'Information Drawer',
1517
child: Flex(
1618
direction: Axis.vertical,
1719
children: <Widget>[
20+
// Build the info panel
1821
_buildInfoPanel(context),
19-
2022
Center(
2123
child: Image.asset('assets/images/utopian-icon.png'),
2224
),
23-
// _buildConnectionPanel(informationBloc),
2425
],
2526
),
2627
);
2728
}
2829

29-
Widget _buildInfoTile(String title, {String subtitle}) {
30-
return ListTile(
31-
title: Text(
32-
title,
33-
textAlign: TextAlign.start,
34-
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
35-
),
36-
subtitle: Text(
37-
subtitle ?? '',
38-
textAlign: TextAlign.start,
39-
style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w600),
40-
),
41-
);
42-
}
43-
30+
// create the info panel
4431
Widget _buildInfoPanel(BuildContext context) {
4532
if (!infoStreamSnapshot.hasData) {
4633
return Flexible(
@@ -52,6 +39,7 @@ class InformationDrawer extends StatelessWidget {
5239
children: [
5340
Container(
5441
padding: EdgeInsets.only(top: 30.0),
42+
// expand the panel to the entire size of the screen width
5543
width: MediaQuery.of(context).size.width,
5644
color: Colors.black,
5745
child: FittedBox(
@@ -66,6 +54,7 @@ class InformationDrawer extends StatelessWidget {
6654
),
6755
),
6856
),
57+
// create all of the info boxes using [_buildInfoTile]
6958
_buildInfoTile(
7059
'${infoStreamSnapshot.data.appName}',
7160
subtitle:
@@ -80,6 +69,7 @@ class InformationDrawer extends StatelessWidget {
8069
subtitle:
8170
'Developed by @Tensor. Many thanks to @Amosbastian for creating the original website: utopian.rocks and to the folks over at utopian.io',
8271
),
72+
// Create button for checking to see if the application can be updated.
8373
RaisedButton(
8474
child: Text('Check for Update'),
8575
onPressed: () => _getNewRelease(context),
@@ -88,10 +78,30 @@ class InformationDrawer extends StatelessWidget {
8878
);
8979
}
9080

81+
// build generic listTiles for the info tiles. [subtitle] is optional.
82+
Widget _buildInfoTile(String title, {String subtitle}) {
83+
return ListTile(
84+
title: Text(
85+
title,
86+
textAlign: TextAlign.start,
87+
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
88+
),
89+
subtitle: Text(
90+
subtitle ?? '',
91+
textAlign: TextAlign.start,
92+
style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w600),
93+
),
94+
);
95+
}
96+
97+
// function to check if a new release is available.
9198
_getNewRelease(BuildContext context) {
9299
final informationBloc = InformationProvider.of(context);
100+
// listen to the releases stream to get the github release json
93101
informationBloc.releases.listen((releases) {
102+
// check tagname against the application version number
94103
if (infoStreamSnapshot.data.version.toString() != releases.tagName) {
104+
// create a dialog window for downloading the new update
95105
showDialog(
96106
context: context,
97107
builder: (BuildContext context) => AlertDialog(
@@ -103,7 +113,7 @@ class InformationDrawer extends StatelessWidget {
103113
actions: <Widget>[
104114
FlatButton(
105115
child: Text('Download'),
106-
onPressed: () => _launchUrl(releases.htmlUrl),
116+
onPressed: () => launchUrl(releases.htmlUrl),
107117
),
108118
FlatButton(
109119
child: Text('Close'),
@@ -112,6 +122,7 @@ class InformationDrawer extends StatelessWidget {
112122
],
113123
));
114124
} else {
125+
// tell the user that there is no new release
115126
showDialog(
116127
context: context,
117128
builder: (context) => AlertDialog(
@@ -129,13 +140,4 @@ class InformationDrawer extends StatelessWidget {
129140
}
130141
});
131142
}
132-
133-
void _launchUrl(String url) async {
134-
if (await canLaunch(url)) {
135-
print('Launching: $url');
136-
await launch(url);
137-
} else {
138-
print('Could not launch $url');
139-
}
140-
}
141143
}

0 commit comments

Comments
 (0)