feed.dart 7.22 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 18 19 20
    return new ScrollableList<FitnessItem>(
      padding: const EdgeDims.all(4.0),
      items: items,
      itemExtent: kFitnessItemHeight,
21
      itemBuilder: (BuildContext context, FitnessItem item, int index) => item.toRow(onDismissed: onDismissed)
22 23 24 25
    );
  }
}

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

  List<Widget> children;
  Function onPressed;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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