blob: 5db150eaa386a3ab686435a699ca8809682e740d [file] [log] [blame]
// 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.
part of fitness;
class FitnessItemList extends Component {
FitnessItemList({ Key key, this.items, this.onDismissed }) : super(key: key) {
assert(items != null);
assert(onDismissed != null);
}
final List<FitnessItem> items;
final FitnessItemHandler onDismissed;
Widget build() {
return new Material(
type: MaterialType.canvas,
child: new ScrollableList<FitnessItem>(
padding: const EdgeDims.all(4.0),
items: items,
itemExtent: kFitnessItemHeight,
itemBuilder: (item) => item.toRow(onDismissed: onDismissed)
)
);
}
}
class DialogMenuItem extends ButtonBase {
DialogMenuItem(this.children, { Key key, this.onPressed }) : super(key: key);
List<Widget> children;
Function onPressed;
void syncConstructorArguments(DialogMenuItem source) {
children = source.children;
onPressed = source.onPressed;
super.syncConstructorArguments(source);
}
Widget buildContent() {
return new Listener(
onGestureTap: (_) {
if (onPressed != null)
onPressed();
},
child: new Container(
height: 48.0,
child: new InkWell(
child: new Padding(
padding: const EdgeDims.symmetric(horizontal: 16.0),
child: new Flex(children)
)
)
)
);
}
}
class FeedFragment extends StatefulComponent {
FeedFragment({ this.navigator, this.userData, this.onItemCreated, this.onItemDeleted });
Navigator navigator;
UserData userData;
FitnessItemHandler onItemCreated;
FitnessItemHandler onItemDeleted;
FitnessMode _fitnessMode = FitnessMode.feed;
void initState() {
// if (debug)
// new Timer(new Duration(seconds: 1), dumpState);
super.initState();
}
void syncConstructorArguments(FeedFragment source) {
navigator = source.navigator;
userData = source.userData;
onItemCreated = source.onItemCreated;
onItemDeleted = source.onItemDeleted;
}
AnimationStatus _snackBarStatus = AnimationStatus.dismissed;
bool _isShowingSnackBar = false;
EventDisposition _handleFitnessModeChange(FitnessMode value) {
setState(() {
_fitnessMode = value;
_drawerShowing = false;
});
return EventDisposition.processed;
}
Drawer buildDrawer() {
if (_drawerStatus == AnimationStatus.dismissed)
return null;
return new Drawer(
showing: _drawerShowing,
level: 3,
onDismissed: _handleDrawerDismissed,
navigator: navigator,
children: [
new DrawerHeader(child: new Text('Fitness')),
new DrawerItem(
icon: 'action/view_list',
onPressed: () => _handleFitnessModeChange(FitnessMode.feed),
selected: _fitnessMode == FitnessMode.feed,
child: new Text('Feed')),
new DrawerItem(
icon: 'action/assessment',
onPressed: () => _handleFitnessModeChange(FitnessMode.chart),
selected: _fitnessMode == FitnessMode.chart,
child: new Text('Chart')),
new DrawerDivider(),
new DrawerItem(
icon: 'action/settings',
onPressed: _handleShowSettings,
child: new Text('Settings')),
new DrawerItem(
icon: 'action/help',
child: new Text('Help & Feedback'))
]
);
}
bool _drawerShowing = false;
AnimationStatus _drawerStatus = AnimationStatus.dismissed;
void _handleOpenDrawer() {
setState(() {
_drawerShowing = true;
_drawerStatus = AnimationStatus.forward;
});
}
void _handleDrawerDismissed() {
setState(() {
_drawerStatus = AnimationStatus.dismissed;
});
}
EventDisposition _handleShowSettings() {
navigator.pop();
navigator.pushNamed('/settings');
return EventDisposition.processed;
}
// TODO(jackson): We should be localizing
String get fitnessModeTitle {
switch(_fitnessMode) {
case FitnessMode.feed: return "Feed";
case FitnessMode.chart: return "Chart";
}
}
Widget buildToolBar() {
return new ToolBar(
left: new IconButton(
icon: "navigation/menu",
onPressed: _handleOpenDrawer),
center: new Text(fitnessModeTitle)
);
}
FitnessItem _undoItem;
void _handleItemDismissed(FitnessItem item) {
onItemDeleted(item);
setState(() {
_undoItem = item;
_isShowingSnackBar = true;
_snackBarStatus = AnimationStatus.forward;
});
}
Widget buildChart() {
double startX;
double endX;
double startY;
double endY;
List<Point> dataSet = new List<Point>();
for (FitnessItem item in userData.items) {
if (item is Measurement) {
double x = item.when.millisecondsSinceEpoch.toDouble();
double y = item.weight;
if (startX == null || startX > x)
startX = x;
if (endX == null || endX < x)
endX = x;
if (startY == null || startY > y)
startY = y;
if (endY == null || endY < y)
endY = y;
dataSet.add(new Point(x, y));
}
}
if (userData.goalWeight != null && userData.goalWeight > 0.0) {
startY = math.min(startY, userData.goalWeight);
endY = math.max(endY, userData.goalWeight);
}
playfair.ChartData data = new playfair.ChartData(
startX: startX,
startY: startY,
endX: endX,
endY: endY,
dataSet: dataSet,
numHorizontalGridlines: 5,
roundToPlaces: 1,
indicatorLine: userData.goalWeight,
indicatorText: "GOAL WEIGHT"
);
return new playfair.Chart(data: data);
}
Widget buildBody() {
TextStyle style = Theme.of(this).text.title;
if (userData == null)
return new Material(type: MaterialType.canvas);
if (userData.items.length == 0)
return new Material(
type: MaterialType.canvas,
child: new Flex(
[new Text("No data yet.\nAdd some!", style: style)],
justifyContent: FlexJustifyContent.center
)
);
switch (_fitnessMode) {
case FitnessMode.feed:
return new FitnessItemList(
items: userData.items.reversed.toList(),
onDismissed: _handleItemDismissed
);
case FitnessMode.chart:
return new Material(
type: MaterialType.canvas,
child: new Container(
padding: const EdgeDims.all(20.0),
child: buildChart()
)
);
}
}
void _handleUndo() {
onItemCreated(_undoItem);
setState(() {
_undoItem = null;
_isShowingSnackBar = false;
});
}
Anchor _snackBarAnchor = new Anchor();
Widget buildSnackBar() {
if (_snackBarStatus == AnimationStatus.dismissed)
return null;
return new SnackBar(
showing: _isShowingSnackBar,
anchor: _snackBarAnchor,
content: new Text("Item deleted."),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); }
);
}
void _handleActionButtonPressed() {
showDialog(navigator, (navigator) => new AddItemDialog(navigator)).then((routeName) {
if (routeName != null)
navigator.pushNamed(routeName);
});
}
Widget buildFloatingActionButton() {
switch (_fitnessMode) {
case FitnessMode.feed:
return _snackBarAnchor.build(
new FloatingActionButton(
child: new Icon(type: 'content/add', size: 24),
onPressed: _handleActionButtonPressed
));
case FitnessMode.chart:
return null;
}
}
Widget build() {
return new Scaffold(
toolbar: buildToolBar(),
body: buildBody(),
snackBar: buildSnackBar(),
floatingActionButton: buildFloatingActionButton(),
drawer: buildDrawer()
);
}
}
class AddItemDialog extends StatefulComponent {
AddItemDialog(this.navigator);
Navigator navigator;
void syncConstructorArguments(AddItemDialog source) {
this.navigator = source.navigator;
}
// TODO(jackson): Internationalize
static final Map<String, String> _labels = {
'/measurements/new': 'Measure',
'/meals/new': 'Eat',
};
String _addItemRoute = _labels.keys.first;
void _handleAddItemRouteChanged(String routeName) {
setState(() {
_addItemRoute = routeName;
});
}
Widget build() {
List<Widget> menuItems = [];
for(String routeName in _labels.keys) {
menuItems.add(new DialogMenuItem([
new Flexible(child: new Text(_labels[routeName])),
new Radio(value: routeName, groupValue: _addItemRoute, onChanged: _handleAddItemRouteChanged),
], onPressed: () => _handleAddItemRouteChanged(routeName)));
}
return new Dialog(
title: new Text("What are you doing?"),
content: new Block(menuItems),
onDismiss: navigator.pop,
actions: [
new FlatButton(
child: new Text('CANCEL'),
onPressed: navigator.pop
),
new FlatButton(
child: new Text('ADD'),
onPressed: () {
navigator.pop(_addItemRoute);
}
),
]
);
}
}