stock_home.dart 8.7 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 stocks;
6 7 8

typedef void ModeUpdater(StockMode mode);

9 10
enum StockHomeTab { market, portfolio }

Matt Perry's avatar
Matt Perry committed
11
class StockHome extends StatefulComponent {
12
  const StockHome(this.stocks, this.symbols, this.configuration, this.updater);
13

Hixie's avatar
Hixie committed
14 15
  final Map<String, Stock> stocks;
  final List<String> symbols;
16 17
  final StockConfiguration configuration;
  final ValueChanged<StockConfiguration> updater;
18

19 20 21 22
  StockHomeState createState() => new StockHomeState();
}

class StockHomeState extends State<StockHome> {
23

24
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
25 26
  bool _isSearching = false;
  String _searchQuery;
27

28
  void _handleSearchBegin() {
Hixie's avatar
Hixie committed
29 30
    ModalRoute.of(context).addLocalHistoryEntry(new LocalHistoryEntry(
      onRemove: () {
Adam Barth's avatar
Adam Barth committed
31 32 33 34 35 36
        setState(() {
          _isSearching = false;
          _searchQuery = null;
        });
      }
    ));
37 38 39 40 41 42
    setState(() {
      _isSearching = true;
    });
  }

  void _handleSearchEnd() {
Hixie's avatar
Hixie committed
43
    Navigator.pop(context);
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
  }

  void _handleSearchQueryChanged(String query) {
    setState(() {
      _searchQuery = query;
    });
  }

  bool _autorefresh = false;
  void _handleAutorefreshChanged(bool value) {
    setState(() {
      _autorefresh = value;
    });
  }

Adam Barth's avatar
Adam Barth committed
59
  void _handleStockModeChange(StockMode value) {
60 61
    if (config.updater != null)
      config.updater(config.configuration.copyWith(stockMode: value));
62 63
  }

64
  void _handleMenuShow() {
Adam Barth's avatar
Adam Barth committed
65 66
    showStockMenu(
      context: context,
67 68 69 70 71
      autorefresh: _autorefresh,
      onAutorefreshChanged: _handleAutorefreshChanged
    );
  }

72 73
  Widget _buildDrawer(BuildContext context) {
    return new Drawer(
Hixie's avatar
Hixie committed
74
      child: new Block(<Widget>[
75
        new DrawerHeader(child: new Text('Stocks')),
76 77 78
        new DrawerItem(
          icon: 'action/assessment',
          selected: true,
79 80
          child: new Text('Stock List')
        ),
81 82
        new DrawerItem(
          icon: 'action/account_balance',
83
          onPressed: () {
Adam Barth's avatar
Adam Barth committed
84 85 86
            showDialog(
              context: context,
              child: new Dialog(
87 88
                title: new Text('Not Implemented'),
                content: new Text('This feature has not yet been implemented.'),
Hixie's avatar
Hixie committed
89
                actions: <Widget>[
90 91 92
                  new FlatButton(
                    child: new Text('USE IT'),
                    onPressed: () {
Hixie's avatar
Hixie committed
93
                      Navigator.pop(context, false);
94 95 96 97 98
                    }
                  ),
                  new FlatButton(
                    child: new Text('OH WELL'),
                    onPressed: () {
Hixie's avatar
Hixie committed
99
                      Navigator.pop(context, false);
100 101 102
                    }
                  ),
                ]
Adam Barth's avatar
Adam Barth committed
103 104
              )
            );
105
          },
106 107
          child: new Text('Account Balance')
        ),
108 109
        new DrawerItem(
          icon: 'device/dvr',
110
          onPressed: () { debugDumpApp(); debugDumpRenderTree(); debugDumpLayerTree(); },
111 112
          child: new Text('Dump App to Console')
        ),
113 114 115 116
        new DrawerDivider(),
        new DrawerItem(
          icon: 'action/thumb_up',
          onPressed: () => _handleStockModeChange(StockMode.optimistic),
117 118 119 120 121 122
          child: new Row(
            children: <Widget>[
              new Flexible(child: new Text('Optimistic')),
              new Radio<StockMode>(value: StockMode.optimistic, groupValue: config.configuration.stockMode, onChanged: _handleStockModeChange)
            ]
          )
123
        ),
124 125 126
        new DrawerItem(
          icon: 'action/thumb_down',
          onPressed: () => _handleStockModeChange(StockMode.pessimistic),
127 128 129 130 131 132
          child: new Row(
            children: <Widget>[
              new Flexible(child: new Text('Pessimistic')),
              new Radio<StockMode>(value: StockMode.pessimistic, groupValue: config.configuration.stockMode, onChanged: _handleStockModeChange)
            ]
          )
133
        ),
134 135 136 137
        new DrawerDivider(),
        new DrawerItem(
          icon: 'action/settings',
          onPressed: _handleShowSettings,
138
          child: new Text('Settings')),
139 140
        new DrawerItem(
          icon: 'action/help',
141
          child: new Text('Help & Feedback'))
142
      ])
143 144 145
    );
  }

Adam Barth's avatar
Adam Barth committed
146
  void _handleShowSettings() {
Hixie's avatar
Hixie committed
147
    Navigator.popAndPushNamed(context, '/settings');
148 149 150 151
  }

  Widget buildToolBar() {
    return new ToolBar(
Hans Muller's avatar
Hans Muller committed
152
      elevation: 0,
153
      center: new Text(StockStrings.of(context).title()),
Hixie's avatar
Hixie committed
154
      right: <Widget>[
155 156
        new IconButton(
          icon: "action/search",
Hixie's avatar
Hixie committed
157 158
          onPressed: _handleSearchBegin,
          tooltip: 'Search'
159
        ),
160 161
        new IconButton(
          icon: "navigation/more_vert",
Hixie's avatar
Hixie committed
162 163
          onPressed: _handleMenuShow,
          tooltip: 'Show menu'
164
        )
165
      ],
166 167 168 169 170
      tabBar: new TabBar<StockHomeTab>(
        labels: <StockHomeTab, TabLabel>{
          StockHomeTab.market: new TabLabel(text: StockStrings.of(context).market()),
          StockHomeTab.portfolio: new TabLabel(text: StockStrings.of(context).portfolio())
        }
171
      )
172
    );
173 174
  }

Hixie's avatar
Hixie committed
175
  Iterable<Stock> _getStockList(Iterable<String> symbols) {
176 177
    return symbols.map((String symbol) => config.stocks[symbol])
        .where((Stock stock) => stock != null);
178 179 180 181 182 183
  }

  Iterable<Stock> _filterBySearchQuery(Iterable<Stock> stocks) {
    if (_searchQuery == null)
      return stocks;
    RegExp regexp = new RegExp(_searchQuery, caseSensitive: false);
Hixie's avatar
Hixie committed
184
    return stocks.where((Stock stock) => stock.symbol.contains(regexp));
185 186
  }

Hixie's avatar
Hixie committed
187 188 189 190 191
  void _buyStock(Stock stock, Key arrowKey) {
    setState(() {
      stock.percentChange = 100.0 * (1.0 / stock.lastSale);
      stock.lastSale += 1.0;
    });
192
    _scaffoldKey.currentState.showSnackBar(new SnackBar(
Hixie's avatar
Hixie committed
193 194 195 196 197 198 199
      content: new Text("Purchased ${stock.symbol} for ${stock.lastSale}"),
      actions: <SnackBarAction>[
        new SnackBarAction(label: "BUY MORE", onPressed: () { _buyStock(stock, arrowKey); })
      ]
    ));
  }

200
  Widget _buildStockList(BuildContext context, Iterable<Stock> stocks, StockHomeTab tab) {
201
    return new StockList(
202
      keySalt: tab,
203
      stocks: stocks.toList(),
Hixie's avatar
Hixie committed
204
      onAction: _buyStock,
Hixie's avatar
Hixie committed
205 206 207
      onOpen: (Stock stock, Key arrowKey) {
        Set<Key> mostValuableKeys = new Set<Key>();
        mostValuableKeys.add(arrowKey);
Hixie's avatar
Hixie committed
208
        Navigator.pushNamed(context, '/stock/${stock.symbol}', mostValuableKeys: mostValuableKeys);
209 210
      },
      onShow: (Stock stock, Key arrowKey) {
211
        _scaffoldKey.currentState.showBottomSheet((BuildContext context) => new StockSymbolBottomSheet(stock: stock));
212 213
      }
    );
214 215
  }

216 217 218 219 220 221 222
  Widget _buildStockTab(BuildContext context, StockHomeTab tab, List<String> stockSymbols) {
    return new Container(
      key: new ValueKey<StockHomeTab>(tab),
      child: _buildStockList(context, _filterBySearchQuery(_getStockList(stockSymbols)).toList(), tab)
    );
  }

Hixie's avatar
Hixie committed
223 224
  static const List<String> portfolioSymbols = const <String>["AAPL","FIZZ", "FIVE", "FLAT", "ZINC", "ZNGA"];

Eric Seidel's avatar
Eric Seidel committed
225
  static GlobalKey searchFieldKey = new GlobalKey();
Hixie's avatar
Hixie committed
226
  static GlobalKey companyNameKey = new GlobalKey();
Eric Seidel's avatar
Eric Seidel committed
227

228 229 230 231
  // TODO(abarth): Should we factor this into a SearchBar in the framework?
  Widget buildSearchBar() {
    return new ToolBar(
      left: new IconButton(
232
        icon: 'navigation/arrow_back',
Adam Barth's avatar
Adam Barth committed
233
        colorFilter: new ColorFilter.mode(Theme.of(context).accentColor, ui.TransferMode.srcATop),
Hixie's avatar
Hixie committed
234 235
        onPressed: _handleSearchEnd,
        tooltip: 'Back'
236 237
      ),
      center: new Input(
Eric Seidel's avatar
Eric Seidel committed
238
        key: searchFieldKey,
239 240 241
        placeholder: 'Search stocks',
        onChanged: _handleSearchQueryChanged
      ),
242
      backgroundColor: Theme.of(context).canvasColor
243 244 245
    );
  }

Hixie's avatar
Hixie committed
246 247 248
  void _handleCreateCompany() {
    showModalBottomSheet(
      // TODO(ianh): Fill this out.
Adam Barth's avatar
Adam Barth committed
249
      context: context,
250
      builder: (BuildContext context) {
251 252 253 254 255 256 257 258
        return new Column(
          children: <Widget>[
            new Input(
              key: companyNameKey,
              placeholder: 'Company Name'
            ),
          ]
        );
259
      }
Matt Perry's avatar
Matt Perry committed
260
    );
261 262 263
  }

  Widget buildFloatingActionButton() {
264
    return new FloatingActionButton(
265
      child: new Icon(icon: 'content/add'),
266
      backgroundColor: Colors.redAccent[200],
Hixie's avatar
Hixie committed
267
      onPressed: _handleCreateCompany
268
    );
269 270
  }

271
  Widget build(BuildContext context) {
272 273
    return new TabBarSelection<StockHomeTab>(
      values: <StockHomeTab>[StockHomeTab.market, StockHomeTab.portfolio],
274 275 276 277 278
      child: new Scaffold(
        key: _scaffoldKey,
        toolBar: _isSearching ? buildSearchBar() : buildToolBar(),
        floatingActionButton: buildFloatingActionButton(),
        drawer: _buildDrawer(context),
Adam Barth's avatar
Adam Barth committed
279 280 281 282 283
        body: new TabBarView(
          children: <Widget>[
            _buildStockTab(context, StockHomeTab.market, config.symbols),
            _buildStockTab(context, StockHomeTab.portfolio, portfolioSymbols),
          ]
284
        )
285
      )
286
    );
287 288
  }
}