提交 2c1be2f2 编写于 作者: E 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
上级 91f2cc00
// 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% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册