feed.dart 7.21 KB
Newer Older
1 2 3 4
// 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.

5
part of fitness;
6

7
class FitnessItemList extends StatelessComponent {
8 9 10 11
  FitnessItemList({ Key key, this.items, this.onDismissed }) : super(key: key) {
    assert(items != null);
    assert(onDismissed != null);
  }
12

13 14
  final List<FitnessItem> items;
  final FitnessItemHandler onDismissed;
15

16
  Widget build(BuildContext context) {
17
    return new ScrollableList(
18 19
      padding: const EdgeDims.all(4.0),
      itemExtent: kFitnessItemHeight,
20
      children: items.map((FitnessItem item) => item.toRow(onDismissed: onDismissed))
21 22 23 24
    );
  }
}

25
class DialogMenuItem extends StatelessComponent {
26 27 28 29 30
  DialogMenuItem(this.children, { Key key, this.onPressed }) : super(key: key);

  List<Widget> children;
  Function onPressed;

31
  Widget build(BuildContext context) {
32 33 34 35 36 37
    return new Container(
      height: 48.0,
      child: new InkWell(
        onTap: onPressed,
        child: new Padding(
          padding: const EdgeDims.symmetric(horizontal: 16.0),
38
          child: new Row(children: children)
39 40 41 42 43 44
        )
      )
    );
  }
}

45
class FeedFragment extends StatefulComponent {
Ian Hickson's avatar
Ian Hickson committed
46
  FeedFragment({ this.userData, this.onItemCreated, this.onItemDeleted });
47

48 49 50
  final UserData userData;
  final FitnessItemHandler onItemCreated;
  final FitnessItemHandler onItemDeleted;
51

52 53
  FeedFragmentState createState() => new FeedFragmentState();
}
54

55 56
class FeedFragmentState extends State<FeedFragment> {
  FitnessMode _fitnessMode = FitnessMode.feed;
57

Adam Barth's avatar
Adam Barth committed
58
  void _handleFitnessModeChange(FitnessMode value) {
59 60 61
    setState(() {
      _fitnessMode = value;
    });
Hixie's avatar
Hixie committed
62
    Navigator.pop(context);
63 64
  }

65 66
  Widget _buildDrawer() {
    return new Drawer(
67
      child: new Block(children: <Widget>[
68
        new DrawerHeader(child: new Text('Fitness')),
69
        new DrawerItem(
70
          icon: 'action/view_list',
71 72
          onPressed: () => _handleFitnessModeChange(FitnessMode.feed),
          selected: _fitnessMode == FitnessMode.feed,
73
          child: new Text('Feed')),
74
        new DrawerItem(
75 76 77
          icon: 'action/assessment',
          onPressed: () => _handleFitnessModeChange(FitnessMode.chart),
          selected: _fitnessMode == FitnessMode.chart,
78
          child: new Text('Chart')),
79 80 81 82
        new DrawerDivider(),
        new DrawerItem(
          icon: 'action/settings',
          onPressed: _handleShowSettings,
83
          child: new Text('Settings')),
84 85
        new DrawerItem(
          icon: 'action/help',
86
          child: new Text('Help & Feedback'))
87
      ])
88 89 90
    );
  }

Adam Barth's avatar
Adam Barth committed
91
  void _handleShowSettings() {
Hixie's avatar
Hixie committed
92
    Navigator.popAndPushNamed(context, '/settings');
93 94 95 96 97
  }

  // TODO(jackson): We should be localizing
  String get fitnessModeTitle {
    switch(_fitnessMode) {
98 99
      case FitnessMode.feed: return "Feed";
      case FitnessMode.chart: return "Chart";
100 101 102 103 104 105 106 107 108
    }
  }

  Widget buildToolBar() {
    return new ToolBar(
      center: new Text(fitnessModeTitle)
    );
  }

109
  void _handleItemDismissed(FitnessItem item) {
110
    config.onItemDeleted(item);
Hixie's avatar
Hixie committed
111
    Scaffold.of(context).showSnackBar(new SnackBar(
112
      content: new Text("Item deleted."),
Hixie's avatar
Hixie committed
113 114 115 116 117 118
      actions: <SnackBarAction>[
        new SnackBarAction(label: "UNDO", onPressed: () {
          config.onItemCreated(item);
        }),
      ]
    ));
119 120
  }

121 122 123 124 125 126
  Widget buildChart() {
    double startX;
    double endX;
    double startY;
    double endY;
    List<Point> dataSet = new List<Point>();
127
    for (FitnessItem item in config.userData.items) {
128 129 130
      if (item is Measurement) {
          double x = item.when.millisecondsSinceEpoch.toDouble();
          double y = item.weight;
131
          if (startX == null || startX > x)
132
            startX = x;
133
          if (endX == null || endX < x)
134 135 136 137 138 139 140 141
          endX = x;
          if (startY == null || startY > y)
            startY = y;
          if (endY == null || endY < y)
            endY = y;
          dataSet.add(new Point(x, y));
      }
    }
142 143 144
    if (config.userData.goalWeight != null && config.userData.goalWeight > 0.0) {
      startY = math.min(startY, config.userData.goalWeight);
      endY = math.max(endY, config.userData.goalWeight);
145
    }
146 147 148 149 150
    playfair.ChartData data = new playfair.ChartData(
      startX: startX,
      startY: startY,
      endX: endX,
      endY: endY,
151 152
      dataSet: dataSet,
      numHorizontalGridlines: 5,
153
      roundToPlaces: 1,
154
      indicatorLine: config.userData.goalWeight,
155
      indicatorText: "GOAL WEIGHT"
156 157 158 159
    );
    return new playfair.Chart(data: data);
  }

160
  Widget buildBody() {
161 162
    TextStyle style = Theme.of(context).text.title;
    if (config.userData == null)
163 164 165
      return new Container();
    if (config.userData.items.length == 0) {
      return new Row(
166
        children: <Widget>[new Text("No data yet.\nAdd some!", style: style)],
167
        justifyContent: FlexJustifyContent.center
168
      );
169
    }
170
    switch (_fitnessMode) {
171
      case FitnessMode.feed:
172
        return new FitnessItemList(
173
          items: config.userData.items.reversed.toList(),
174
          onDismissed: _handleItemDismissed
175
        );
176
      case FitnessMode.chart:
177 178 179
        return new Container(
          padding: const EdgeDims.all(20.0),
          child: buildChart()
180 181 182 183
        );
    }
  }

184
  void _handleActionButtonPressed() {
Adam Barth's avatar
Adam Barth committed
185
    showDialog(context: context, child: new AddItemDialog()).then((routeName) {
186
      if (routeName != null)
Hixie's avatar
Hixie committed
187
        Navigator.pushNamed(context, routeName);
188 189 190 191 192 193
    });
  }

  Widget buildFloatingActionButton() {
    switch (_fitnessMode) {
      case FitnessMode.feed:
Adam Barth's avatar
Adam Barth committed
194
        return new FloatingActionButton(
195
          child: new Icon(icon: 'content/add'),
Adam Barth's avatar
Adam Barth committed
196 197
          onPressed: _handleActionButtonPressed
        );
198 199 200 201 202
      case FitnessMode.chart:
        return null;
    }
  }

203
  Widget build(BuildContext context) {
204
    return new Scaffold(
Adam Barth's avatar
Adam Barth committed
205
      toolBar: buildToolBar(),
206
      body: buildBody(),
207 208
      floatingActionButton: buildFloatingActionButton(),
      drawer: _buildDrawer()
209 210 211 212 213
    );
  }
}

class AddItemDialog extends StatefulComponent {
214 215
  AddItemDialogState createState() => new AddItemDialogState();
}
216

217
class AddItemDialogState extends State<AddItemDialog> {
218
  // TODO(jackson): Internationalize
Hixie's avatar
Hixie committed
219
  static final Map<String, String> _labels = <String, String>{
220 221 222 223 224
    '/measurements/new': 'Measure',
    '/meals/new': 'Eat',
  };

  String _addItemRoute = _labels.keys.first;
225 226 227 228 229 230

  void _handleAddItemRouteChanged(String routeName) {
    setState(() {
        _addItemRoute = routeName;
    });
  }
231

232
  Widget build(BuildContext context) {
Hixie's avatar
Hixie committed
233 234 235
    List<Widget> menuItems = <Widget>[];
    for (String routeName in _labels.keys) {
      menuItems.add(new DialogMenuItem(<Widget>[
236
        new Flexible(child: new Text(_labels[routeName])),
Hixie's avatar
Hixie committed
237
        new Radio<String>(value: routeName, groupValue: _addItemRoute, onChanged: _handleAddItemRouteChanged),
238 239
      ], onPressed: () => _handleAddItemRouteChanged(routeName)));
    }
240
    return new Dialog(
241
      title: new Text("What are you doing?"),
242
      content: new Block(children: menuItems),
Hixie's avatar
Hixie committed
243
      actions: <Widget>[
244 245
        new FlatButton(
          child: new Text('CANCEL'),
Adam Barth's avatar
Adam Barth committed
246
          onPressed: () {
Hixie's avatar
Hixie committed
247
            Navigator.pop(context);
Adam Barth's avatar
Adam Barth committed
248
          }
249 250
        ),
        new FlatButton(
251
          child: new Text('ADD'),
252
          onPressed: () {
Hixie's avatar
Hixie committed
253
            Navigator.pop(context, _addItemRoute);
254 255 256 257
          }
        ),
      ]
    );
258 259
  }
}