From 319f539b48a1a444c48039835fe431fb462df5bd Mon Sep 17 00:00:00 2001 From: Andreas Fahrecker Date: Fri, 4 Dec 2020 05:15:04 +0100 Subject: [PATCH] Feature/BugFix-01 Future Time Progresses (#4) - Fixed Bug with Future Time Progresses Now no longer shows future time progresses in dashboard or in app drawer - Dashboard Now Shows started and future times. These cards are divided based on their count - Fixed Bug Future Progress In Detail Screen - Progress Detail Screen Now Shows in how many Days a progress starts - BugFix App Version Signed-off-by: Andreas Fahrecker --- lib/app.dart | 19 +++- lib/main.dart | 2 + lib/screens/progress_creation_screen.dart | 9 +- lib/screens/progress_dashboard_screen.dart | 119 ++++++++++++++++++--- lib/screens/progress_detail_screen.dart | 93 +++++++++++----- lib/selectors/time_progress_selectors.dart | 14 +++ lib/widgets/app_drawer_widget.dart | 39 +++++-- pubspec.lock | 7 ++ pubspec.yaml | 3 +- 9 files changed, 245 insertions(+), 60 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index 6772be1..fe3c3b1 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -10,8 +10,13 @@ class TimeProgressTrackerApp extends StatelessWidget { static const String name = "Time Progress Tracker"; final Store store; + final String appVersion; - TimeProgressTrackerApp({Key key, this.store}) : super(key: key); + TimeProgressTrackerApp({ + Key key, + this.store, + this.appVersion, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -26,11 +31,17 @@ class TimeProgressTrackerApp extends StatelessWidget { initialRoute: ProgressDashboardScreen.routeName, routes: { ProgressDashboardScreen.routeName: (BuildContext context) => - ProgressDashboardScreen(), + ProgressDashboardScreen( + appVersion: appVersion, + ), ProgressDetailScreen.routeName: (BuildContext context) => - ProgressDetailScreen(), + ProgressDetailScreen( + appVersion: appVersion, + ), ProgressCreationScreen.routeName: (BuildContext context) => - ProgressCreationScreen(), + ProgressCreationScreen( + appVersion: appVersion, + ), }, ), ); diff --git a/lib/main.dart b/lib/main.dart index db4686f..af21c77 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:package_info/package_info.dart'; import 'package:redux/redux.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:time_progress_tracker/app.dart'; @@ -18,5 +19,6 @@ Future main() async { TimeProgressRepository(await SharedPreferences.getInstance()), ), ), + appVersion: (await PackageInfo.fromPlatform()).version, )); } diff --git a/lib/screens/progress_creation_screen.dart b/lib/screens/progress_creation_screen.dart index bf69031..e838a94 100644 --- a/lib/screens/progress_creation_screen.dart +++ b/lib/screens/progress_creation_screen.dart @@ -11,6 +11,11 @@ import 'package:time_progress_tracker/widgets/app_drawer_widget.dart'; class ProgressCreationScreen extends StatefulWidget { static const routeName = "/progress-creation"; + final String appVersion; + + ProgressCreationScreen({Key key, @required this.appVersion}) + : super(key: key); + @override State createState() { return _ProgressCreationScreenState(); @@ -60,7 +65,9 @@ class _ProgressCreationScreenState extends State { appBar: AppBar( title: Text("Create Time Progress"), ), - drawer: AppDrawer(), + drawer: AppDrawer( + appVersion: widget.appVersion, + ), body: Container( padding: EdgeInsets.all(8), child: Column( diff --git a/lib/screens/progress_dashboard_screen.dart b/lib/screens/progress_dashboard_screen.dart index ce8dfc2..922ad99 100644 --- a/lib/screens/progress_dashboard_screen.dart +++ b/lib/screens/progress_dashboard_screen.dart @@ -7,19 +7,31 @@ import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/screens/progress_creation_screen.dart'; import 'package:time_progress_tracker/screens/progress_detail_screen.dart'; +import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; import 'package:time_progress_tracker/widgets/app_drawer_widget.dart'; class ProgressDashboardScreen extends StatelessWidget { static const routeName = "/progress-dashboard"; static const title = "Time Progress Dashboard"; + final String appVersion; + + ProgressDashboardScreen({ + Key key, + @required this.appVersion, + }) : super(key: key); + @override Widget build(BuildContext context) { + AppBar appBar = AppBar( + title: Text(title), + ); + return Scaffold( - appBar: AppBar( - title: Text(title), + appBar: appBar, + drawer: AppDrawer( + appVersion: appVersion, ), - drawer: AppDrawer(), body: StoreConnector( converter: _ViewModel.fromStore, onInit: loadTimeProgressListIfUnloaded, @@ -29,11 +41,11 @@ class ProgressDashboardScreen extends StatelessWidget { child: CircularProgressIndicator(), ); } - List dashboardTileList = List(); - if (vm.timeProgressList.length > 0) { - for (TimeProgress tp in vm.timeProgressList) { - dashboardTileList.add( + List startedProgressesTileList = List(); + if (vm.hasStartedProgresses) { + for (TimeProgress tp in vm.startedTimeProgreses) { + startedProgressesTileList.add( Card( child: ListTile( title: Text(tp.name), @@ -53,20 +65,82 @@ class ProgressDashboardScreen extends StatelessWidget { ), ); } - } else { - dashboardTileList.add(ListTile( - title: Text("You don't have any tracked Progress."), + } + + List futureProgressesTileList = List(); + if (vm.hasFutureProgresses) { + for (TimeProgress tp in vm.futureTimeProgresses) { + futureProgressesTileList.add( + Card( + child: ListTile( + title: Text(tp.name), + subtitle: Text( + "Starts in ${tp.startTime.difference(DateTime.now()).inDays} Days."), + onTap: () { + Navigator.pushNamed( + context, ProgressDetailScreen.routeName, + arguments: ProgressDetailScreenArguments(tp.id)); + }, + ), + ), + ); + } + } + + double dividerHeight = 1; + double screenHeight = MediaQuery.of(context).size.height - + appBar.preferredSize.height - + 24 - + dividerHeight; //Divider + + List columnChildren = List(); + int tpCount = + vm.startedTimeProgreses.length + vm.futureTimeProgresses.length; + if (vm.hasStartedProgresses) { + columnChildren.add(Container( + height: vm.hasFutureProgresses + ? (screenHeight / tpCount) * vm.startedTimeProgreses.length + : screenHeight, + child: ListView( + padding: EdgeInsets.all(8), + children: startedProgressesTileList, + ), + )); + } + if (vm.hasStartedProgresses && vm.hasFutureProgresses) { + columnChildren.add(Divider( + height: dividerHeight, + )); + } + if (vm.hasFutureProgresses) { + columnChildren.add(Container( + height: vm.hasStartedProgresses + ? (screenHeight / tpCount) * vm.futureTimeProgresses.length + : screenHeight, + child: ListView( + padding: EdgeInsets.all(8), + children: futureProgressesTileList, + ), )); } - return ListView( - padding: EdgeInsets.all(8), - children: dashboardTileList, + if (!vm.hasStartedProgresses && !vm.hasFutureProgresses) { + columnChildren.add(Container( + margin: EdgeInsets.all(16), + child: Center( + child: Text("You don't have any tracked Progress."), + ), + )); + } + + return Column( + children: columnChildren, ); }, ), floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButton: FloatingActionButton( + heroTag: "createProgressBTN", child: Icon(Icons.add), onPressed: () { Navigator.pushNamed(context, ProgressCreationScreen.routeName); @@ -77,17 +151,30 @@ class ProgressDashboardScreen extends StatelessWidget { } class _ViewModel { - final List timeProgressList; + final List startedTimeProgreses; + final bool hasStartedProgresses; + final List futureTimeProgresses; + final bool hasFutureProgresses; final bool hasLoaded; _ViewModel({ - @required this.timeProgressList, + @required this.startedTimeProgreses, + @required this.hasStartedProgresses, + @required this.futureTimeProgresses, + @required this.hasFutureProgresses, @required this.hasLoaded, }); static _ViewModel fromStore(Store store) { + List startedTPList = + startedTimeProgressesSelector(store.state); + List furtureTPList = + futureTimeProgressesSelector(store.state); return _ViewModel( - timeProgressList: store.state.timeProgressList, + startedTimeProgreses: startedTPList, + hasStartedProgresses: startedTPList.length > 0, + futureTimeProgresses: furtureTPList, + hasFutureProgresses: furtureTPList.length > 0, hasLoaded: store.state.hasLoaded, ); } diff --git a/lib/screens/progress_detail_screen.dart b/lib/screens/progress_detail_screen.dart index 949c6cd..42f46b0 100644 --- a/lib/screens/progress_detail_screen.dart +++ b/lib/screens/progress_detail_screen.dart @@ -24,6 +24,13 @@ class ProgressDetailScreenArguments { class ProgressDetailScreen extends StatefulWidget { static const routeName = "/progress-detail"; + final String appVersion; + + ProgressDetailScreen({ + Key key, + @required this.appVersion, + }) : super(key: key); + @override State createState() { return _ProgressDetailScreenState(); @@ -147,7 +154,9 @@ class _ProgressDetailScreenState extends State { appBar: AppBar( title: Text("Progress"), ), - drawer: AppDrawer(), + drawer: AppDrawer( + appVersion: widget.appVersion, + ), body: Container( margin: EdgeInsets.all(8), child: StoreConnector( @@ -170,32 +179,58 @@ class _ProgressDetailScreenState extends State { : "The Name of the Time Progress has to be set.", ), ) - : FittedBox( - fit: BoxFit.fitWidth, - child: Text( - vm.timeProgress.name, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87, + : vm.hasProgressStarted + ? FittedBox( + fit: BoxFit.fitWidth, + child: Text( + vm.timeProgress.name, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ) + : Center( + child: FittedBox( + fit: BoxFit.fitWidth, + child: Text( + vm.timeProgress.name, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ), ), - ), + ), + vm.hasProgressStarted + ? Expanded( + flex: 2, + child: ProgressDetailCircularPercent( + percentDone: _isBeingEdited + ? _editedProgress.percentDone() + : vm.timeProgress.percentDone(), ), - ), - Expanded( - flex: 2, - child: ProgressDetailCircularPercent( - percentDone: _isBeingEdited - ? _editedProgress.percentDone() - : vm.timeProgress.percentDone(), - ), - ), - Expanded( - flex: 1, - child: ProgressDetailLinearPercent( - timeProgress: - _isBeingEdited ? _editedProgress : vm.timeProgress, - ), - ), + ) + : Expanded( + flex: 2, + child: Text( + "Starts in ${vm.timeProgress.startTime.difference(DateTime.now()).inDays} Days."), + ), + vm.hasProgressStarted + ? Expanded( + flex: 1, + child: ProgressDetailLinearPercent( + timeProgress: _isBeingEdited + ? _editedProgress + : vm.timeProgress, + ), + ) + : Spacer( + flex: 1, + ), Expanded( flex: 1, child: Text( @@ -242,15 +277,19 @@ class _ProgressDetailScreenState extends State { class _ViewModel { final TimeProgress timeProgress; + final bool hasProgressStarted; _ViewModel({ @required this.timeProgress, + @required this.hasProgressStarted, }); static _ViewModel fromStoreAndArg( Store store, ProgressDetailScreenArguments args) { + TimeProgress tp = timeProgressByIdSelector(store.state, args.id); return _ViewModel( - timeProgress: timeProgressByIdSelector(store.state, args.id), - ); + timeProgress: tp, + hasProgressStarted: DateTime.now().millisecondsSinceEpoch > + tp.startTime.millisecondsSinceEpoch); } } diff --git a/lib/selectors/time_progress_selectors.dart b/lib/selectors/time_progress_selectors.dart index ae81e7b..9b14d31 100644 --- a/lib/selectors/time_progress_selectors.dart +++ b/lib/selectors/time_progress_selectors.dart @@ -4,6 +4,20 @@ import 'package:time_progress_tracker/models/time_progress.dart'; List timeProgressListSelector(AppState state) => state.timeProgressList; +List startedTimeProgressesSelector(AppState state) => + state.timeProgressList + .where((timeProgress) => + DateTime.now().millisecondsSinceEpoch >= + timeProgress.startTime.millisecondsSinceEpoch) + .toList(); + +List futureTimeProgressesSelector(AppState state) => + state.timeProgressList + .where((timeProgress) => + DateTime.now().millisecondsSinceEpoch < + timeProgress.startTime.millisecondsSinceEpoch) + .toList(); + TimeProgress timeProgressByIdSelector(AppState state, String id) { if (state.timeProgressList.length < 1) return null; return state.timeProgressList diff --git a/lib/widgets/app_drawer_widget.dart b/lib/widgets/app_drawer_widget.dart index faf04a5..a02d890 100644 --- a/lib/widgets/app_drawer_widget.dart +++ b/lib/widgets/app_drawer_widget.dart @@ -9,8 +9,16 @@ import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/screens/progress_dashboard_screen.dart'; import 'package:time_progress_tracker/screens/progress_detail_screen.dart'; +import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; class AppDrawer extends StatelessWidget { + final String appVersion; + + AppDrawer({ + Key key, + @required this.appVersion, + }) : super(key: key); + @override Widget build(BuildContext context) { return Drawer( @@ -18,6 +26,11 @@ class AppDrawer extends StatelessWidget { converter: _ViewModel.fromStore, onInit: loadTimeProgressListIfUnloaded, builder: (context, _ViewModel vm) { + if (!vm.hasLoaded) { + return Center( + child: CircularProgressIndicator(), + ); + } List drawerTileList = List(); drawerTileList.add(DrawerHeader( child: Text(TimeProgressTrackerApp.name), @@ -36,8 +49,8 @@ class AppDrawer extends StatelessWidget { }, ), )); - if (vm.timeProgressList.length > 0) { - for (TimeProgress tp in vm.timeProgressList) { + if (vm.startedTimeProgresses.length > 0) { + for (TimeProgress tp in vm.startedTimeProgresses) { drawerTileList.add(ListTile( title: Text(tp.name), trailing: CircularPercentIndicator( @@ -60,7 +73,7 @@ class AppDrawer extends StatelessWidget { ); }, )); - if (vm.timeProgressList.last != tp) { + if (vm.startedTimeProgresses.last != tp) { drawerTileList.add(Divider( color: Colors.black12, )); @@ -80,11 +93,10 @@ class AppDrawer extends StatelessWidget { title: Text("About"), onTap: () { showAboutDialog( - context: context, - applicationName: TimeProgressTrackerApp.name, - applicationVersion: ' Version 0.0.1', - applicationLegalese: '\u00a9Andreas Fahrecker 2020' - ); + context: context, + applicationName: TimeProgressTrackerApp.name, + applicationVersion: " Version $appVersion", + applicationLegalese: '\u00a9Andreas Fahrecker 2020'); }, ), )); @@ -98,13 +110,18 @@ class AppDrawer extends StatelessWidget { } class _ViewModel { - final List timeProgressList; + final List startedTimeProgresses; + final bool hasLoaded; - _ViewModel({@required this.timeProgressList}); + _ViewModel({ + @required this.startedTimeProgresses, + @required this.hasLoaded, + }); static _ViewModel fromStore(Store store) { return _ViewModel( - timeProgressList: store.state.timeProgressList, + startedTimeProgresses: startedTimeProgressesSelector(store.state), + hasLoaded: store.state.hasLoaded, ); } } diff --git a/pubspec.lock b/pubspec.lock index 2462b92..043a91f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -156,6 +156,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.3" + package_info: + dependency: "direct main" + description: + name: package_info + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3+2" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5ae61ed..1105192 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.0.2+2 +version: 0.0.3+3 environment: sdk: ">=2.7.0 <3.0.0" @@ -25,6 +25,7 @@ dependencies: sdk: flutter flutter_redux: meta: + package_info: percent_indicator: redux: shared_preferences: