Commit b2212785 authored by Eric Seidel's avatar Eric Seidel

Make Fitness tiles nicer

Moved from Cards to Tiles and made the printing
of the dates nicer by using some code from a Dart SDK example:
https://github.com/dart-lang/sdk/blob/master/samples-dev/swarm/swarm_ui_lib/util/DateUtils.dart

I also built a UserData class to help keep saving/sorting
consistent as well as fixed the sort order to have most
recent at the top.

@abarth
parent 441fe4d4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Forked from https://github.com/dart-lang/sdk/blob/master/samples-dev/swarm/swarm_ui_lib/util/DateUtils.dart
class DateUtils {
static const WEEKDAYS = const ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday'];
static const YESTERDAY = 'Yesterday';
static const MS_IN_WEEK =
DateTime.DAYS_PER_WEEK * Duration.MILLISECONDS_PER_DAY;
/** Formats a time in H:MM A format */
static String toHourMinutesString(Duration duration) {
assert(duration.inDays == 0);
int hours = duration.inHours;
String a;
if (hours >= 12) {
a = 'pm';
if (hours != 12) {
hours -= 12;
}
} else {
a = 'am';
if (hours == 0) {
hours += 12;
}
}
String twoDigits(int n) {
if (n >= 10) return "${n}";
return "0${n}";
}
String mm =
twoDigits(duration.inMinutes.remainder(Duration.MINUTES_PER_HOUR));
return "${hours}:${mm} ${a}";
}
/**
* A date/time formatter that takes into account the current date/time:
* - if it's from today, just show the time
* - if it's from yesterday, just show 'Yesterday'
* - if it's from the same week, just show the weekday
* - otherwise, show just the date
*/
static String toRecentTimeString(DateTime then) {
bool datesAreEqual(DateTime d1, DateTime d2) {
return (d1.year == d2.year) && (d1.month == d2.month) &&
(d1.day == d2.day);
}
final now = new DateTime.now();
if (datesAreEqual(then, now)) {
return toHourMinutesString(new Duration(
days: 0,
hours: then.hour,
minutes: then.minute,
seconds: then.second,
milliseconds: then.millisecond));
}
final today = new DateTime(now.year, now.month, now.day, 0, 0, 0, 0);
Duration delta = today.difference(then);
if (delta.inMilliseconds < Duration.MILLISECONDS_PER_DAY) {
return YESTERDAY;
} else if (delta.inMilliseconds < MS_IN_WEEK) {
return WEEKDAYS[then.weekday];
} else {
// TODO(jmesserly): locale specific date format
String twoDigits(int n) {
if (n >= 10) return "${n}";
return "0${n}";
}
String twoDigitMonth = twoDigits(then.month);
String twoDigitDay = twoDigits(then.day);
return "${then.year}-${twoDigitMonth}-${twoDigitDay}";
}
}
}
......@@ -6,7 +6,9 @@ part of fitness;
typedef void FitnessItemHandler(FitnessItem item);
const double kFitnessItemHeight = 79.0;
// TODO(eseidel): This should be a constant on a SingleLineTile class
// https://www.google.com/design/spec/components/lists.html#lists-specs
const double kFitnessItemHeight = 48.0;
abstract class FitnessItem {
FitnessItem.fromJson(Map json) : when = DateTime.parse(json['when']);
......@@ -19,7 +21,7 @@ abstract class FitnessItem {
Map toJson() => { 'when' : when.toIso8601String() };
// TODO(jackson): Internationalize
String get displayDate => "${when.year.toString()}-${when.month.toString().padLeft(2,'0')}-${when.day.toString().padLeft(2,'0')}";
String get displayDate => DateUtils.toRecentTimeString(when);
FitnessItemRow toRow({ FitnessItemHandler onDismissed });
}
......@@ -40,12 +42,19 @@ abstract class FitnessItemRow extends Component {
Widget build() {
return new Dismissable(
onDismissed: () => onDismissed(item),
child: new Card(
child: new Container(
height: kFitnessItemHeight,
padding: const EdgeDims.all(8.0),
child: buildContent()
)
child: new Container(
height: kFitnessItemHeight,
// TODO(eseidel): Padding top should be 16px for a single-line tile:
// https://www.google.com/design/spec/components/lists.html#lists-specs
padding: const EdgeDims.all(10.0),
// TODO(eseidel): This line should be drawn by the list as it should
// stay put even when the tile is dismissed!
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: Theme.of(this).dividerColor)
)
),
child: buildContent()
)
);
}
......
......@@ -9,6 +9,8 @@ import 'package:sky/painting/text_style.dart';
import 'package:sky/theme/colors.dart' as colors;
import 'package:sky/widgets.dart';
import 'user_data.dart';
import 'date_utils.dart';
import 'dart:async';
part 'feed.dart';
part 'fitness_item.dart';
......@@ -17,23 +19,47 @@ part 'meal.dart';
part 'measurement.dart';
part 'settings.dart';
class FitnessApp extends App {
class UserData {
List<FitnessItem> _items = [];
List<FitnessItem> get items => _items;
void set items(List<FitnessItem> newItems) {
_items = [];
_items.addAll(newItems);
sort();
}
void sort() {
_items.sort((a, b) => -a.when.compareTo(b.when));
}
void addAndSave(FitnessItem item) {
_items.add(item);
sort();
save();
}
void removeAndSave(FitnessItem item) {
_items.remove(item);
save();
}
Future save() => saveFitnessData(_items);
}
class FitnessApp extends App {
NavigationState _navigationState;
final List<FitnessItem> _userData = [];
final UserData _userData = new UserData();
void didMount() {
super.didMount();
loadFitnessData().then((List<Measurement> list) {
setState(() {
_userData.addAll(list);
});
setState(() => _userData.items = list);
}).catchError((e) => print("Failed to load data: $e"));
}
void save() {
saveFitnessData(_userData)
.catchError((e) => print("Failed to load data: $e"));
_userData.save().catchError((e) => print("Failed to load data: $e"));
}
void initState() {
......@@ -42,7 +68,7 @@ class FitnessApp extends App {
name: '/',
builder: (navigator, route) => new FeedFragment(
navigator: navigator,
userData: _userData,
userData: _userData.items,
onItemCreated: _handleItemCreated,
onItemDeleted: _handleItemDeleted
)
......@@ -71,27 +97,18 @@ class FitnessApp extends App {
void onBack() {
if (_navigationState.hasPrevious()) {
setState(() {
_navigationState.pop();
});
setState(() => _navigationState.pop());
} else {
super.onBack();
}
}
void _handleItemCreated(FitnessItem item) {
setState(() {
_userData.add(item);
_userData.sort((a, b) => a.when.compareTo(b.when));
save();
});
setState(() => _userData.addAndSave(item));
}
void _handleItemDeleted(FitnessItem item) {
setState(() {
_userData.remove(item);
saveFitnessData(_userData);
});
setState(() => _userData.removeAndSave(item));
}
BackupMode backupSetting = BackupMode.disabled;
......
......@@ -35,7 +35,7 @@ class MeasurementRow extends FitnessItemRow {
new Flexible(
child: new Text(
measurement.displayWeight,
style: const TextStyle(textAlign: TextAlign.right)
style: Theme.of(this).text.subhead
)
),
new Flexible(
......
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