stock_home.dart 8.26 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 9 10

typedef void ModeUpdater(StockMode mode);

const Duration _kSnackbarSlideDuration = const Duration(milliseconds: 200);

Matt Perry's avatar
Matt Perry committed
11
class StockHome extends StatefulComponent {
12 13 14 15 16 17 18 19

  StockHome(this.navigator, this.stocks, this.stockMode, this.modeUpdater);

  Navigator navigator;
  List<Stock> stocks;
  StockMode stockMode;
  ModeUpdater modeUpdater;

20
  void syncConstructorArguments(StockHome source) {
21 22 23 24 25 26 27 28 29
    navigator = source.navigator;
    stocks = source.stocks;
    stockMode = source.stockMode;
    modeUpdater = source.modeUpdater;
  }

  bool _isSearching = false;
  String _searchQuery;

30
  AnimationStatus _snackBarStatus = AnimationStatus.dismissed;
31
  bool _isSnackBarShowing = false;
32 33 34 35 36 37 38 39 40 41 42 43 44 45

  void _handleSearchBegin() {
    navigator.pushState(this, (_) {
      setState(() {
        _isSearching = false;
        _searchQuery = null;
      });
    });
    setState(() {
      _isSearching = true;
    });
  }

  void _handleSearchEnd() {
46 47
    assert(navigator.currentRoute is RouteState);
    assert((navigator.currentRoute as RouteState).owner == this); // TODO(ianh): remove cast once analyzer is cleverer
48 49 50 51 52 53 54 55 56 57 58 59 60 61
    navigator.pop();
    setState(() {
      _isSearching = false;
      _searchQuery = null;
    });
  }

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

  bool _drawerShowing = false;
62
  AnimationStatus _drawerStatus = AnimationStatus.dismissed;
63 64 65 66

  void _handleOpenDrawer() {
    setState(() {
      _drawerShowing = true;
67
      _drawerStatus = AnimationStatus.forward;
68 69 70
    });
  }

71
  void _handleDrawerDismissed() {
72
    setState(() {
73
      _drawerStatus = AnimationStatus.dismissed;
74 75 76 77
    });
  }

  bool _menuShowing = false;
78
  AnimationStatus _menuStatus = AnimationStatus.dismissed;
79 80 81 82

  void _handleMenuShow() {
    setState(() {
      _menuShowing = true;
83
      _menuStatus = AnimationStatus.forward;
84 85 86 87 88 89 90 91 92
    });
  }

  void _handleMenuHide() {
    setState(() {
      _menuShowing = false;
    });
  }

93
  void _handleMenuDismissed() {
94
    setState(() {
95
      _menuStatus = AnimationStatus.dismissed;
96 97 98 99 100 101 102 103 104 105
    });
  }

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

106
  EventDisposition _handleStockModeChange(StockMode value) {
107 108 109 110 111
    setState(() {
      stockMode = value;
    });
    if (modeUpdater != null)
      modeUpdater(value);
112
    return EventDisposition.processed;
113 114 115
  }

  Drawer buildDrawer() {
116
    if (_drawerStatus == AnimationStatus.dismissed)
117
      return null;
Matt Perry's avatar
Matt Perry committed
118
    assert(_drawerShowing); // TODO(mpcomplete): this is always true
119 120 121
    return new Drawer(
      level: 3,
      showing: _drawerShowing,
122
      onDismissed: _handleDrawerDismissed,
123 124
      navigator: navigator,
      children: [
125
        new DrawerHeader(child: new Text('Stocks')),
126 127 128
        new DrawerItem(
          icon: 'action/assessment',
          selected: true,
129 130
          child: new Text('Stock List')
        ),
131 132
        new DrawerItem(
          icon: 'action/account_balance',
133 134
          child: new Text('Account Balance')
        ),
135 136 137 138
        new DrawerDivider(),
        new DrawerItem(
          icon: 'action/thumb_up',
          onPressed: () => _handleStockModeChange(StockMode.optimistic),
139
          child: new Row([
140 141
            new Flexible(child: new Text('Optimistic')),
            new Radio(value: StockMode.optimistic, groupValue: stockMode, onChanged: _handleStockModeChange)
142 143
          ])
        ),
144 145 146
        new DrawerItem(
          icon: 'action/thumb_down',
          onPressed: () => _handleStockModeChange(StockMode.pessimistic),
147
          child: new Row([
148 149
            new Flexible(child: new Text('Pessimistic')),
            new Radio(value: StockMode.pessimistic, groupValue: stockMode, onChanged: _handleStockModeChange)
150 151
          ])
        ),
152 153 154 155
        new DrawerDivider(),
        new DrawerItem(
          icon: 'action/settings',
          onPressed: _handleShowSettings,
156
          child: new Text('Settings')),
157 158
        new DrawerItem(
          icon: 'action/help',
159
          child: new Text('Help & Feedback'))
160 161 162 163
     ]
    );
  }

164
  EventDisposition _handleShowSettings() {
165 166
    navigator.pop();
    navigator.pushNamed('/settings');
167
    return EventDisposition.processed;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
  }

  Widget buildToolBar() {
    return new ToolBar(
        left: new IconButton(
          icon: "navigation/menu",
          onPressed: _handleOpenDrawer),
        center: new Text('Stocks'),
        right: [
          new IconButton(
            icon: "action/search",
            onPressed: _handleSearchBegin),
          new IconButton(
            icon: "navigation/more_vert",
            onPressed: _handleMenuShow)
        ]
      );
  }

  int selectedTabIndex = 0;
  List<String> portfolioSymbols = ["AAPL","FIZZ", "FIVE", "FLAT", "ZINC", "ZNGA"];

  Iterable<Stock> _filterByPortfolio(Iterable<Stock> stocks) {
    return stocks.where((stock) => portfolioSymbols.contains(stock.symbol));
  }

  Iterable<Stock> _filterBySearchQuery(Iterable<Stock> stocks) {
    if (_searchQuery == null)
      return stocks;
    RegExp regexp = new RegExp(_searchQuery, caseSensitive: false);
    return stocks.where((stock) => stock.symbol.contains(regexp));
  }

  Widget buildMarketStockList() {
    return new Stocklist(stocks: _filterBySearchQuery(stocks).toList());
  }

  Widget buildPortfolioStocklist() {
    return new Stocklist(stocks: _filterBySearchQuery(_filterByPortfolio(stocks)).toList());
  }

  Widget buildTabNavigator() {
    List<TabNavigatorView> views = <TabNavigatorView>[
      new TabNavigatorView(
        label: const TabLabel(text: 'MARKET'),
        builder: buildMarketStockList
      ),
      new TabNavigatorView(
        label: const TabLabel(text: 'PORTFOLIO'),
        builder: buildPortfolioStocklist
      )
    ];
    return new TabNavigator(
      views: views,
      selectedIndex: selectedTabIndex,
      onChanged: (tabIndex) {
        setState(() { selectedTabIndex = tabIndex; } );
      }
    );
  }

Eric Seidel's avatar
Eric Seidel committed
229 230
  static GlobalKey searchFieldKey = new GlobalKey();

231 232 233 234 235 236 237 238 239
  // TODO(abarth): Should we factor this into a SearchBar in the framework?
  Widget buildSearchBar() {
    return new ToolBar(
      left: new IconButton(
        icon: "navigation/arrow_back",
        color: Theme.of(this).accentColor,
        onPressed: _handleSearchEnd
      ),
      center: new Input(
Eric Seidel's avatar
Eric Seidel committed
240
        key: searchFieldKey,
241 242 243 244 245 246 247 248 249
        placeholder: 'Search stocks',
        onChanged: _handleSearchQueryChanged
      ),
      backgroundColor: Theme.of(this).canvasColor
    );
  }

  void _handleUndo() {
    setState(() {
Matt Perry's avatar
Matt Perry committed
250
      _isSnackBarShowing = false;
251 252 253
    });
  }

254
  Anchor _snackBarAnchor = new Anchor();
255
  Widget buildSnackBar() {
256
    if (_snackBarStatus == AnimationStatus.dismissed)
257
      return null;
Matt Perry's avatar
Matt Perry committed
258 259
    return new SnackBar(
      showing: _isSnackBarShowing,
260
      anchor: _snackBarAnchor,
Matt Perry's avatar
Matt Perry committed
261 262
      content: new Text("Stock purchased!"),
      actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
263
      onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); }
Matt Perry's avatar
Matt Perry committed
264
    );
265 266 267 268
  }

  void _handleStockPurchased() {
    setState(() {
Matt Perry's avatar
Matt Perry committed
269
      _isSnackBarShowing = true;
270
      _snackBarStatus = AnimationStatus.forward;
271 272 273 274
    });
  }

  Widget buildFloatingActionButton() {
275 276 277 278 279 280
    return _snackBarAnchor.build(
      new FloatingActionButton(
        child: new Icon(type: 'content/add', size: 24),
        backgroundColor: colors.RedAccent[200],
        onPressed: _handleStockPurchased
      ));
281 282 283
  }

  void addMenuToOverlays(List<Widget> overlays) {
284
    if (_menuStatus == AnimationStatus.dismissed)
285 286 287 288
      return;
    overlays.add(new ModalOverlay(
      children: [new StockMenu(
        showing: _menuShowing,
289
        onDismissed: _handleMenuDismissed,
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
        navigator: navigator,
        autorefresh: _autorefresh,
        onAutorefreshChanged: _handleAutorefreshChanged
      )],
      onDismiss: _handleMenuHide));
  }

  Widget build() {
    List<Widget> overlays = [
      new Scaffold(
        toolbar: _isSearching ? buildSearchBar() : buildToolBar(),
        body: buildTabNavigator(),
        snackBar: buildSnackBar(),
        floatingActionButton: buildFloatingActionButton(),
        drawer: buildDrawer()
      ),
    ];
    addMenuToOverlays(overlays);
308
    return new Stack(overlays);
309 310
  }
}