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 <AndreasFahrecker@gmail.com>
This commit is contained in:
Andreas Fahrecker 2020-12-04 05:15:04 +01:00 committed by GitHub
parent 988e8f3c72
commit 319f539b48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 245 additions and 60 deletions

View File

@ -10,8 +10,13 @@ class TimeProgressTrackerApp extends StatelessWidget {
static const String name = "Time Progress Tracker";
final Store<AppState> 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,
),
},
),
);

View File

@ -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<void> main() async {
TimeProgressRepository(await SharedPreferences.getInstance()),
),
),
appVersion: (await PackageInfo.fromPlatform()).version,
));
}

View File

@ -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<StatefulWidget> createState() {
return _ProgressCreationScreenState();
@ -60,7 +65,9 @@ class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
appBar: AppBar(
title: Text("Create Time Progress"),
),
drawer: AppDrawer(),
drawer: AppDrawer(
appVersion: widget.appVersion,
),
body: Container(
padding: EdgeInsets.all(8),
child: Column(

View File

@ -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) {
return Scaffold(
appBar: AppBar(
AppBar appBar = AppBar(
title: Text(title),
);
return Scaffold(
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<Widget> dashboardTileList = List<Widget>();
if (vm.timeProgressList.length > 0) {
for (TimeProgress tp in vm.timeProgressList) {
dashboardTileList.add(
List<Widget> startedProgressesTileList = List<Widget>();
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<Widget> futureProgressesTileList = List<Widget>();
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<Widget> columnChildren = List<Widget>();
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<TimeProgress> timeProgressList;
final List<TimeProgress> startedTimeProgreses;
final bool hasStartedProgresses;
final List<TimeProgress> 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<AppState> store) {
List<TimeProgress> startedTPList =
startedTimeProgressesSelector(store.state);
List<TimeProgress> 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,
);
}

View File

@ -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<StatefulWidget> createState() {
return _ProgressDetailScreenState();
@ -147,7 +154,9 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
appBar: AppBar(
title: Text("Progress"),
),
drawer: AppDrawer(),
drawer: AppDrawer(
appVersion: widget.appVersion,
),
body: Container(
margin: EdgeInsets.all(8),
child: StoreConnector(
@ -170,10 +179,24 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
: "The Name of the Time Progress has to be set.",
),
)
: FittedBox(
: 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,
@ -181,20 +204,32 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
),
),
),
Expanded(
),
vm.hasProgressStarted
? Expanded(
flex: 2,
child: ProgressDetailCircularPercent(
percentDone: _isBeingEdited
? _editedProgress.percentDone()
: vm.timeProgress.percentDone(),
),
)
: Expanded(
flex: 2,
child: Text(
"Starts in ${vm.timeProgress.startTime.difference(DateTime.now()).inDays} Days."),
),
Expanded(
vm.hasProgressStarted
? Expanded(
flex: 1,
child: ProgressDetailLinearPercent(
timeProgress:
_isBeingEdited ? _editedProgress : vm.timeProgress,
timeProgress: _isBeingEdited
? _editedProgress
: vm.timeProgress,
),
)
: Spacer(
flex: 1,
),
Expanded(
flex: 1,
@ -242,15 +277,19 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
class _ViewModel {
final TimeProgress timeProgress;
final bool hasProgressStarted;
_ViewModel({
@required this.timeProgress,
@required this.hasProgressStarted,
});
static _ViewModel fromStoreAndArg(
Store<AppState> 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);
}
}

View File

@ -4,6 +4,20 @@ import 'package:time_progress_tracker/models/time_progress.dart';
List<TimeProgress> timeProgressListSelector(AppState state) =>
state.timeProgressList;
List<TimeProgress> startedTimeProgressesSelector(AppState state) =>
state.timeProgressList
.where((timeProgress) =>
DateTime.now().millisecondsSinceEpoch >=
timeProgress.startTime.millisecondsSinceEpoch)
.toList();
List<TimeProgress> 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

View File

@ -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<Widget> drawerTileList = List<Widget>();
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,
));
@ -82,9 +95,8 @@ class AppDrawer extends StatelessWidget {
showAboutDialog(
context: context,
applicationName: TimeProgressTrackerApp.name,
applicationVersion: ' Version 0.0.1',
applicationLegalese: '\u00a9Andreas Fahrecker 2020'
);
applicationVersion: " Version $appVersion",
applicationLegalese: '\u00a9Andreas Fahrecker 2020');
},
),
));
@ -98,13 +110,18 @@ class AppDrawer extends StatelessWidget {
}
class _ViewModel {
final List<TimeProgress> timeProgressList;
final List<TimeProgress> startedTimeProgresses;
final bool hasLoaded;
_ViewModel({@required this.timeProgressList});
_ViewModel({
@required this.startedTimeProgresses,
@required this.hasLoaded,
});
static _ViewModel fromStore(Store<AppState> store) {
return _ViewModel(
timeProgressList: store.state.timeProgressList,
startedTimeProgresses: startedTimeProgressesSelector(store.state),
hasLoaded: store.state.hasLoaded,
);
}
}

View File

@ -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:

View File

@ -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: