Commit 247bcbce authored by Administrator's avatar Administrator

streamlining onboarding process

parent 2f66bc22
......@@ -55,23 +55,39 @@ class Activity extends ChangeNotifier {
}
distanceString() {
if (db.totalDistance != null) {
if (db.totalDistance != null)
return (db.totalDistance / 1000).toStringAsFixed(2) + " km";
} else
else
return "";
}
heartRateString() {
if (db.avgHeartRate != null) {
if (db.avgHeartRate != null)
return db.avgHeartRate.toString() + " bpm";
} else
else
return "";
}
timeString() => DateFormat("H:mm").format(db.timeCreated);
timeString() {
if (db.timeCreated != null)
return DateFormat("H:mm").format(db.timeCreated);
else
return "- - -";
}
dateString() => DateFormat("d MMM yy").format(db.timeCreated);
shortDateString() => DateFormat("d.M.").format(db.timeCreated);
dateString() {
if (db.timeCreated != null)
return DateFormat("d MMM yy").format(db.timeCreated);
else
return "- - -";
}
shortDateString() {
if (db.timeCreated != null)
return DateFormat("d.M.").format(db.timeCreated);
else
return "- - -";
}
paceString() => db.avgSpeed.toPace() + "/km";
......@@ -139,7 +155,6 @@ class Activity extends ChangeNotifier {
return db.avgGroundTime;
}
Future<double> get avgGroundTime async {
if (db.avgGroundTime == null) {
List<Event> records = await this.records;
......@@ -462,7 +477,7 @@ class Activity extends ChangeNotifier {
prompt);
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final startDate = now - 20 * 86400;
final startDate = now - 21 * 86400;
List<StravaActivity.SummaryActivity> summaryActivities =
await strava.getLoggedInAthleteActivities(now, startDate);
......
......@@ -28,7 +28,7 @@ class Athlete extends ChangeNotifier {
String get stateText {
switch (db.state) {
case "undefined":
case "new":
return "Loading athlete data from Strava ...";
case "unsaved":
return "Strava data loaded successfully.";
......@@ -43,6 +43,7 @@ class Athlete extends ChangeNotifier {
final storage = FlutterSecureStorage();
await storage.write(key: "email", value: email);
await storage.write(key: "password", value: password);
notifyListeners();
}
readCredentials() async {
......@@ -58,5 +59,4 @@ class Athlete extends ChangeNotifier {
}
get activities => Activity.all(athlete: this);
}
......@@ -14,125 +14,29 @@ class Dashboard extends StatefulWidget {
}
class _DashboardState extends State<Dashboard> {
Future<List<Athlete>> athletes;
List<Athlete> athletes = [];
@override
void initState() {
athletes = Athlete.all();
getAthletes();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Encrateia Dashboard")),
body: FutureBuilder<List<Athlete>>(
future: athletes,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data.length == 0) {
return ListView(
padding: EdgeInsets.all(20),
children: <Widget>[
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: MyIcon.information,
title: Text('Welcome to Encrateia!'),
subtitle: Text(
'Maybe you want to learn a bit about Encrateia.'
'We have provided some introductory text for you.',
),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Introduction'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
IntroductionScreen(),
),
);
},
)
],
),
],
),
),
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: MyIcon.athlete,
title: Text('Who are you?'),
subtitle: Text(
'This app stores date associated to one athlete '
'(you) or many athletes (if you act as a trainer).',
),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Create a new Athlete'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditAthleteScreen(
athlete: Athlete(),
),
),
);
},
)
],
)
],
))
],
);
} else {
return ListView(
padding: EdgeInsets.all(40),
children: <Widget>[
for (Athlete athlete in snapshot.data)
ListTile(
leading: Image.network(athlete.db.photoPath),
title: Text(
"${athlete.db.firstName} ${athlete.db.lastName} - ${athlete.db.stravaId}"),
subtitle: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
onPressed: () => navigateToListActivitiesScreen(
athlete: athlete),
child: Text("Analyze"),
),
RaisedButton(
onPressed: () =>
navigateToEditAthleteScreen(athlete: athlete),
child: MyIcon.edit,
),
],
),
),
],
);
}
} else {
return CircularProgressIndicator();
}
}),
appBar: AppBar(
title: Text("Encrateia Dashboard"),
),
body: dashboardBody(),
);
}
getAthletes() async {
athletes = await Athlete.all();
setState(() {});
}
navigateToListActivitiesScreen({Athlete athlete}) async {
await athlete.readCredentials();
Navigator.push(
......@@ -150,6 +54,104 @@ class _DashboardState extends State<Dashboard> {
MaterialPageRoute(
builder: (context) => EditAthleteScreen(athlete: athlete),
),
);
).then((_) => getAthletes());
}
dashboardBody() {
if (athletes.length == 0) {
return ListView(
padding: EdgeInsets.all(20),
children: <Widget>[
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: MyIcon.information,
title: Text('Welcome to Encrateia!'),
subtitle: Text(
'Maybe you want to learn a bit about Encrateia.'
'We have provided some introductory text for you.',
),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Introduction'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IntroductionScreen(),
),
);
},
)
],
),
],
),
),
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: MyIcon.athlete,
title: Text('Who are you?'),
subtitle: Text(
'This app stores date associated to one athlete '
'(you) or many athletes (if you act as a trainer).',
),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Create a new Athlete'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditAthleteScreen(
athlete: Athlete(),
),
),
);
},
)
],
)
],
))
],
);
} else {
return ListView(
padding: EdgeInsets.all(40),
children: <Widget>[
for (Athlete athlete in athletes)
ListTile(
leading: Image.network(athlete.db.photoPath),
title: Text(
"${athlete.db.firstName} ${athlete.db.lastName} - ${athlete.db.stravaId}"),
subtitle: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
onPressed: () =>
navigateToListActivitiesScreen(athlete: athlete),
child: Text("Analyze"),
),
RaisedButton(
onPressed: () =>
navigateToEditAthleteScreen(athlete: athlete),
child: MyIcon.edit,
),
],
),
),
],
);
}
}
}
......@@ -21,99 +21,110 @@ class EditAthleteScreen extends StatelessWidget {
body: ChangeNotifierProvider.value(
value: athlete,
child: Consumer<Athlete>(
builder: (context, athlete, _child) => ListView(
padding: EdgeInsets.all(20),
children: <Widget>[
ListTile(
leading: Text("First Name"),
title: Text(athlete.db.firstName ?? 't.b.d.'),
),
ListTile(
leading: Text("Last Name"),
title: Text(athlete.db.lastName ?? 't.b.d.'),
),
ListTile(
leading: Text("Strava ID"),
title: Text(athlete.db.stravaId?.toString() ?? 't.b.d.'),
),
ListTile(
leading: Text("Strava Username"),
title: Text(athlete.db.stravaUsername ?? 't.b.d.'),
),
TextFormField(
decoration: InputDecoration(labelText: "Email"),
initialValue: athlete.email,
onChanged: (value) => athlete.email = value,
),
TextFormField(
decoration: InputDecoration(labelText: "Password"),
onChanged: (value) => athlete.password = value,
initialValue: athlete.password,
obscureText: true,
),
builder: (context, athlete, _child) =>
editAthleteForm(athlete, context),
),
),
);
}
// Strava Connection Card
if (athlete.db == null || athlete.db.stravaId == null)
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: MyIcon.brokenConnection,
title: Text('Strava Connection'),
subtitle: Text('This athlete is not connected to a '
'Strava User yet'),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('CONNECT TO STRAVA'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
StravaGetUser(athlete: athlete),
),
);
},
)
],
Widget editAthleteForm(Athlete athlete, BuildContext context) {
if (athlete.db == null || athlete.db.stravaId == null) {
// Strava Connection Card
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: MyIcon.brokenConnection,
title: Text('Step 1 of 2: Strava Connection'),
subtitle: Text('This athlete is not connected to a '
'Strava User yet'),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('CONNECT TO STRAVA'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StravaGetUser(athlete: athlete),
),
],
),
),
);
},
)
],
),
],
),
);
} else {
return ListView(
padding: EdgeInsets.all(20),
children: <Widget>[
ListTile(
leading: MyIcon.website,
title: Text('Step 2 of 2: Credentials for Strava Web Site scraping'),
),
ListTile(
leading: Text("First Name"),
title: Text(athlete.db.firstName ?? 't.b.d.'),
),
ListTile(
leading: Text("Last Name"),
title: Text(athlete.db.lastName ?? 't.b.d.'),
),
ListTile(
leading: Text("Strava ID"),
title: Text(athlete.db.stravaId?.toString() ?? 't.b.d.'),
),
ListTile(
leading: Text("Strava Username"),
title: Text(athlete.db.stravaUsername ?? 't.b.d.'),
),
TextFormField(
decoration: InputDecoration(labelText: "Email"),
initialValue: athlete.email,
onChanged: (value) => athlete.email = value,
),
TextFormField(
decoration: InputDecoration(labelText: "Password"),
onChanged: (value) => athlete.password = value,
initialValue: athlete.password,
obscureText: true,
),
// Cancel and Save Card
Padding(
padding: EdgeInsets.all(15),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
RaisedButton(
color: Theme.of(context).primaryColorDark,
textColor: Theme.of(context).primaryColorLight,
child: Text('Cancel', textScaleFactor: 1.5),
onPressed: () => Navigator.of(context).pop(),
),
Container(width: 20.0),
RaisedButton(
color: Theme.of(context).primaryColorDark,
textColor: Theme.of(context).primaryColorLight,
child: Text('Save', textScaleFactor: 1.5),
onPressed: () {
athlete.db.save();
athlete.storeCredentials();
Navigator.of(context).pop();
},
),
],
// Cancel and Save Card
Padding(
padding: EdgeInsets.all(15),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
RaisedButton(
color: Theme.of(context).primaryColorDark,
textColor: Theme.of(context).primaryColorLight,
child: Text('Cancel', textScaleFactor: 1.5),
onPressed: () => Navigator.of(context).pop(),
),
Container(width: 20.0),
RaisedButton(
color: Theme.of(context).primaryColorDark,
textColor: Theme.of(context).primaryColorLight,
child: Text('Save', textScaleFactor: 1.5),
onPressed: () => save(context),
),
),
],
],
),
),
),
),
);
],
);
}
}
save (BuildContext context) async {
await athlete.db.save();
await athlete.storeCredentials();
Navigator.of(context).pop();
}
}
......@@ -20,6 +20,7 @@ class StravaGetUser extends StatelessWidget {
body: Consumer<Athlete>(
builder: (context, athlete, _child) {
if (athlete.db.firstName == null) loginToStrava();
if (athlete.db.state == 'fromStrava') Navigator.of(context).pop();
return Container(
child: Padding(
padding: EdgeInsets.all(20),
......
......@@ -82,4 +82,6 @@ class MyIcon {
static final trainingEffect = Icon(Icons.fitness_center);
// V
static final verticalOscillation = Icon(Icons.unfold_more);
// W
static final website = Icon(Icons.web);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment