blob: 599d1cd8677023a837a24e16aeb2dc332d43f4a5 [file] [log] [blame]
// 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.
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show debugDumpRenderTree, debugDumpLayerTree, debugDumpSemanticsTree;
import 'package:flutter/scheduler.dart' show timeDilation;
import 'stock_data.dart';
import 'stock_list.dart';
import 'stock_strings.dart';
import 'stock_symbol_viewer.dart';
import 'stock_types.dart';
typedef void ModeUpdater(StockMode mode);
enum _StockMenuItem { autorefresh, refresh, speedUp, speedDown }
enum StockHomeTab { market, portfolio }
class _NotImplementedDialog extends StatelessComponent {
Widget build(BuildContext context) {
return new Dialog(
title: new Text('Not Implemented'),
content: new Text('This feature has not yet been implemented.'),
actions: <Widget>[
new FlatButton(
child: new Row(
children: <Widget>[
new Icon(
icon: Icons.dvr,
size: 18.0
),
new Container(
width: 8.0
),
new Text('DUMP APP TO CONSOLE'),
]
),
onPressed: () { debugDumpApp(); }
),
new FlatButton(
child: new Text('OH WELL'),
onPressed: () {
Navigator.pop(context, false);
}
)
]
);
}
}
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;
InputValue _searchQuery = InputValue.empty;
bool _autorefresh = false;
void _handleSearchBegin() {
ModalRoute.of(context).addLocalHistoryEntry(new LocalHistoryEntry(
onRemove: () {
setState(() {
_isSearching = false;
_searchQuery = InputValue.empty;
});
}
));
setState(() {
_isSearching = true;
});
}
void _handleSearchEnd() {
Navigator.pop(context);
}
void _handleSearchQueryChanged(InputValue query) {
setState(() {
_searchQuery = query;
});
}
void _handleStockModeChange(StockMode value) {
if (config.updater != null)
config.updater(config.configuration.copyWith(stockMode: value));
}
void _handleStockMenu(BuildContext context, _StockMenuItem value) {
switch(value) {
case _StockMenuItem.autorefresh:
setState(() {
_autorefresh = !_autorefresh;
});
break;
case _StockMenuItem.refresh:
showDialog(
context: context,
child: new _NotImplementedDialog()
);
break;
case _StockMenuItem.speedUp:
timeDilation /= 5.0;
break;
case _StockMenuItem.speedDown:
timeDilation *= 5.0;
break;
}
}
Widget _buildDrawer(BuildContext context) {
return new Drawer(
child: new Block(children: <Widget>[
new DrawerHeader(child: new Text('Stocks')),
new DrawerItem(
icon: Icons.assessment,
selected: true,
child: new Text('Stock List')
),
new DrawerItem(
icon: Icons.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: Icons.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 Divider(),
new DrawerItem(
icon: Icons.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: Icons.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 Divider(),
new DrawerItem(
icon: Icons.settings,
onPressed: _handleShowSettings,
child: new Text('Settings')),
new DrawerItem(
icon: Icons.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: Icons.search,
onPressed: _handleSearchBegin,
tooltip: 'Search'
),
new PopupMenuButton<_StockMenuItem>(
onSelected: (_StockMenuItem value) { _handleStockMenu(context, value); },
items: <PopupMenuItem<_StockMenuItem>>[
new CheckedPopupMenuItem<_StockMenuItem>(
value: _StockMenuItem.autorefresh,
checked: _autorefresh,
child: new Text('Autorefresh')
),
new PopupMenuItem<_StockMenuItem>(
value: _StockMenuItem.refresh,
child: new Text('Refresh')
),
new PopupMenuItem<_StockMenuItem>(
value: _StockMenuItem.speedUp,
child: new Text('Increase animation speed')
),
new PopupMenuItem<_StockMenuItem>(
value: _StockMenuItem.speedDown,
child: new Text('Decrease animation speed')
)
]
)
],
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.text.isEmpty)
return stocks;
RegExp regexp = new RegExp(_searchQuery.text, 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}"),
action: 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"];
// TODO(abarth): Should we factor this into a SearchBar in the framework?
Widget buildSearchBar() {
return new ToolBar(
left: new IconButton(
icon: Icons.arrow_back,
color: Theme.of(context).accentColor,
onPressed: _handleSearchEnd,
tooltip: 'Back'
),
center: new Input(
value: _searchQuery,
autofocus: true,
hintText: 'Search stocks',
onChanged: _handleSearchQueryChanged
),
backgroundColor: Theme.of(context).canvasColor
);
}
void _handleCreateCompany() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) => new _CreateCompanySheet()
);
}
Widget buildFloatingActionButton() {
return new FloatingActionButton(
tooltip: 'Create company',
child: new Icon(icon: Icons.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),
]
)
)
);
}
}
class _CreateCompanySheet extends StatefulComponent {
_CreateCompanySheetState createState() => new _CreateCompanySheetState();
}
class _CreateCompanySheetState extends State<_CreateCompanySheet> {
InputValue _companyName = InputValue.empty;
void _handleCompanyNameChanged(InputValue value) {
setState(() {
_companyName = value;
});
}
Widget build(BuildContext context) {
// TODO(ianh): Fill this out.
return new Column(
children: <Widget>[
new Input(
autofocus: true,
hintText: 'Company Name',
value: _companyName,
onChanged: _handleCompanyNameChanged
),
]
);
}
}