// 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 stocks; typedef void ModeUpdater(StockMode mode); enum StockHomeTab { market, portfolio } class StockHome extends StatefulComponent { const StockHome(this.stocks, this.symbols, this.configuration, this.updater); final Map<String, Stock> stocks; final List<String> symbols; final StockConfiguration configuration; final ValueChanged<StockConfiguration> updater; StockHomeState createState() => new StockHomeState(); } class StockHomeState extends State<StockHome> { final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); bool _isSearching = false; String _searchQuery; void _handleSearchBegin() { ModalRoute.of(context).addLocalHistoryEntry(new LocalHistoryEntry( onRemove: () { setState(() { _isSearching = false; _searchQuery = null; }); } )); setState(() { _isSearching = true; }); } void _handleSearchEnd() { Navigator.pop(context); } void _handleSearchQueryChanged(String query) { setState(() { _searchQuery = query; }); } bool _autorefresh = false; void _handleAutorefreshChanged(bool value) { setState(() { _autorefresh = value; }); } void _handleStockModeChange(StockMode value) { if (config.updater != null) config.updater(config.configuration.copyWith(stockMode: value)); } void _handleMenuShow() { showStockMenu( context: context, autorefresh: _autorefresh, onAutorefreshChanged: _handleAutorefreshChanged ); } Widget _buildDrawer(BuildContext context) { return new Drawer( child: new Block(children: <Widget>[ new DrawerHeader(child: new Text('Stocks')), new DrawerItem( icon: 'action/assessment', selected: true, child: new Text('Stock List') ), new DrawerItem( icon: 'action/account_balance', onPressed: () { showDialog( context: context, child: new Dialog( title: new Text('Not Implemented'), content: new Text('This feature has not yet been implemented.'), actions: <Widget>[ new FlatButton( onPressed: () { Navigator.pop(context, false); }, child: new Text('USE IT') ), new FlatButton( onPressed: () { Navigator.pop(context, false); }, child: new Text('OH WELL') ), ] ) ); }, child: new Text('Account Balance') ), new DrawerItem( icon: 'device/dvr', onPressed: () { try { debugDumpApp(); debugDumpRenderTree(); debugDumpLayerTree(); debugDumpSemanticsTree(); } catch (e, stack) { debugPrint('Exception while dumping app:\n$e\n$stack'); } }, child: new Text('Dump App to Console') ), new DrawerDivider(), new DrawerItem( icon: 'action/thumb_up', onPressed: () => _handleStockModeChange(StockMode.optimistic), child: new Row( children: <Widget>[ new Flexible(child: new Text('Optimistic')), new Radio<StockMode>(value: StockMode.optimistic, groupValue: config.configuration.stockMode, onChanged: _handleStockModeChange) ] ) ), new DrawerItem( icon: 'action/thumb_down', onPressed: () => _handleStockModeChange(StockMode.pessimistic), child: new Row( children: <Widget>[ new Flexible(child: new Text('Pessimistic')), new Radio<StockMode>(value: StockMode.pessimistic, groupValue: config.configuration.stockMode, onChanged: _handleStockModeChange) ] ) ), new DrawerDivider(), new DrawerItem( icon: 'action/settings', onPressed: _handleShowSettings, child: new Text('Settings')), new DrawerItem( icon: 'action/help', child: new Text('Help & Feedback')) ]) ); } void _handleShowSettings() { Navigator.popAndPushNamed(context, '/settings'); } Widget buildToolBar() { return new ToolBar( elevation: 0, center: new Text(StockStrings.of(context).title()), right: <Widget>[ new IconButton( icon: "action/search", onPressed: _handleSearchBegin, tooltip: 'Search' ), new IconButton( icon: "navigation/more_vert", onPressed: _handleMenuShow, tooltip: 'Show menu' ) ], 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()) } ) ); } Iterable<Stock> _getStockList(Iterable<String> symbols) { return symbols.map((String symbol) => config.stocks[symbol]) .where((Stock stock) => stock != null); } Iterable<Stock> _filterBySearchQuery(Iterable<Stock> stocks) { if (_searchQuery == null) return stocks; RegExp regexp = new RegExp(_searchQuery, caseSensitive: false); return stocks.where((Stock stock) => stock.symbol.contains(regexp)); } void _buyStock(Stock stock, Key arrowKey) { setState(() { stock.percentChange = 100.0 * (1.0 / stock.lastSale); stock.lastSale += 1.0; }); _scaffoldKey.currentState.showSnackBar(new SnackBar( content: new Text("Purchased ${stock.symbol} for ${stock.lastSale}"), actions: <SnackBarAction>[ new SnackBarAction(label: "BUY MORE", onPressed: () { _buyStock(stock, arrowKey); }) ] )); } Widget _buildStockList(BuildContext context, Iterable<Stock> stocks, StockHomeTab tab) { return new StockList( keySalt: tab, stocks: stocks.toList(), onAction: _buyStock, onOpen: (Stock stock, Key arrowKey) { Set<Key> mostValuableKeys = new HashSet<Key>(); mostValuableKeys.add(arrowKey); Navigator.pushNamed(context, '/stock/${stock.symbol}', mostValuableKeys: mostValuableKeys); }, onShow: (Stock stock, Key arrowKey) { _scaffoldKey.currentState.showBottomSheet((BuildContext context) => new StockSymbolBottomSheet(stock: stock)); } ); } 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) ); } static const List<String> portfolioSymbols = const <String>["AAPL","FIZZ", "FIVE", "FLAT", "ZINC", "ZNGA"]; static GlobalKey searchFieldKey = new GlobalKey(); static GlobalKey companyNameKey = new GlobalKey(); // 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(context).accentColor, onPressed: _handleSearchEnd, tooltip: 'Back' ), center: new Input( key: searchFieldKey, autofocus: true, hintText: 'Search stocks', onChanged: _handleSearchQueryChanged ), backgroundColor: Theme.of(context).canvasColor ); } void _handleCreateCompany() { showModalBottomSheet( // TODO(ianh): Fill this out. context: context, builder: (BuildContext context) { return new Column( children: <Widget>[ new Input( key: companyNameKey, autofocus: true, hintText: 'Company Name' ), ] ); } ); } Widget buildFloatingActionButton() { return new FloatingActionButton( child: new Icon(icon: 'content/add'), backgroundColor: Colors.redAccent[200], onPressed: _handleCreateCompany ); } Widget build(BuildContext context) { return new TabBarSelection<StockHomeTab>( values: <StockHomeTab>[StockHomeTab.market, StockHomeTab.portfolio], child: new Scaffold( key: _scaffoldKey, toolBar: _isSearching ? buildSearchBar() : buildToolBar(), floatingActionButton: buildFloatingActionButton(), drawer: _buildDrawer(context), body: new TabBarView( children: <Widget>[ _buildStockTab(context, StockHomeTab.market, config.symbols), _buildStockTab(context, StockHomeTab.portfolio, portfolioSymbols), ] ) ) ); } }