diff --git a/CHANGELOG.md b/CHANGELOG.md index 954eb2fc..1a7f75f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.9.4 +- add KSM/DOT prices + +# 0.9.3 +- bug fix + +# 0.9.1 +- add support for opening dApps in webview + # 0.9.0 - add Governance proposals - add Council motions diff --git a/ios/Flutter/.last_build_id b/ios/Flutter/.last_build_id index dcc05942..97b01546 100644 --- a/ios/Flutter/.last_build_id +++ b/ios/Flutter/.last_build_id @@ -1 +1 @@ -21550ad233f9eb1230b6dc6301eb3cf0 \ No newline at end of file +dd15065b86c2890a4fee04c8e16046f0 \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 3fc570b3..80befa15 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -396,7 +396,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.9.3; + MARKETING_VERSION = 0.9.4; PRODUCT_BUNDLE_IDENTIFIER = io.polkawallet.polkawallet; PRODUCT_NAME = Runner; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -531,7 +531,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.9.3; + MARKETING_VERSION = 0.9.4; PRODUCT_BUNDLE_IDENTIFIER = io.polkawallet.polkawallet; PRODUCT_NAME = Runner; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -561,7 +561,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.9.3; + MARKETING_VERSION = 0.9.4; PRODUCT_BUNDLE_IDENTIFIER = io.polkawallet.polkawallet; PRODUCT_NAME = Runner; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/common/consts/settings.dart b/lib/common/consts/settings.dart index c44613fd..4b106b95 100644 --- a/lib/common/consts/settings.dart +++ b/lib/common/consts/settings.dart @@ -129,7 +129,7 @@ const String cross_chain_transfer_address_laminar = '5CLaminarAUSDCrossChainTransferxxxxxxxxxxxxxwisu'; /// app versions -const String app_beta_version = '0.9.3-beta.1'; +const String app_beta_version = '0.9.4-beta.1'; /// js code versions const Map js_code_version_map = { diff --git a/lib/page/assets/asset/assetPage.dart b/lib/page/assets/asset/assetPage.dart index b4c93e97..1d472fc1 100644 --- a/lib/page/assets/asset/assetPage.dart +++ b/lib/page/assets/asset/assetPage.dart @@ -222,6 +222,17 @@ class _AssetPageState extends State }); } + String tokenPrice; + if ((store.settings.endpoint.info == network_name_polkadot || + store.settings.endpoint.info == network_name_kusama) && + store.assets.marketPrices[symbol] != null && + balancesInfo != null) { + tokenPrice = (store.assets.marketPrices[symbol] * + Fmt.bigIntToDouble(balancesInfo.total, + decimals: decimals)) + .toStringAsFixed(4); + } + return Column( children: [ Container( @@ -231,7 +242,8 @@ class _AssetPageState extends State child: Column( children: [ Padding( - padding: EdgeInsets.only(bottom: 16), + padding: EdgeInsets.only( + bottom: tokenPrice != null ? 4 : 16), child: Text( Fmt.token(isBaseToken ? balancesInfo.total : balance, decimals: decimals, length: 8), @@ -242,53 +254,60 @@ class _AssetPageState extends State ), ), ), + tokenPrice != null + ? Padding( + padding: EdgeInsets.only(bottom: 16), + child: Text( + '≈ \$ ${tokenPrice ?? '--.--'}', + style: TextStyle( + color: Theme.of(context).cardColor, + ), + ), + ) + : Container(), isBaseToken - ? Builder( - builder: (_) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - margin: EdgeInsets.only(right: 12), - child: Row( - children: [ - lockedInfo.length > 2 - ? TapTooltip( - message: lockedInfo, - child: Padding( - padding: EdgeInsets.only( - right: 6), - child: Icon( - Icons.info, - size: 16, - color: titleColor, - ), - ), - waitDuration: - Duration(seconds: 0), - ) - : Container(), - Text( - '${dic['locked']}: ${Fmt.token(balancesInfo.lockedBalance, decimals: decimals)}', - style: TextStyle(color: titleColor), - ), - ], - ), - ), - Container( - margin: EdgeInsets.only(right: 12), - child: Text( - '${dic['available']}: ${Fmt.token(balancesInfo.transferable, decimals: decimals)}', + ? Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(right: 12), + child: Row( + children: [ + lockedInfo.length > 2 + ? TapTooltip( + message: lockedInfo, + child: Padding( + padding: + EdgeInsets.only(right: 6), + child: Icon( + Icons.info, + size: 16, + color: titleColor, + ), + ), + waitDuration: + Duration(seconds: 0), + ) + : Container(), + Text( + '${dic['locked']}: ${Fmt.token(balancesInfo.lockedBalance, decimals: decimals)}', style: TextStyle(color: titleColor), ), - ), - Text( - '${dic['reserved']}: ${Fmt.token(balancesInfo.reserved, decimals: decimals)}', - style: TextStyle(color: titleColor), - ), - ], - ); - }, + ], + ), + ), + Container( + margin: EdgeInsets.only(right: 12), + child: Text( + '${dic['available']}: ${Fmt.token(balancesInfo.transferable, decimals: decimals)}', + style: TextStyle(color: titleColor), + ), + ), + Text( + '${dic['reserved']}: ${Fmt.token(balancesInfo.reserved, decimals: decimals)}', + style: TextStyle(color: titleColor), + ), + ], ) : Container(), ], diff --git a/lib/page/assets/index.dart b/lib/page/assets/index.dart index fc9020d8..fe87ade6 100644 --- a/lib/page/assets/index.dart +++ b/lib/page/assets/index.dart @@ -435,6 +435,7 @@ class _AssetsState extends State { bool isLaminar = store.settings.endpoint.info == networkEndpointLaminar.info; bool isPolkadot = store.settings.endpoint.info == network_name_polkadot; + bool isKusama = store.settings.endpoint.info == network_name_kusama; List currencyIds = []; if ((isAcala || isLaminar) && networkName != null) { @@ -460,6 +461,12 @@ class _AssetsState extends State { style: TextStyle(fontStyle: FontStyle.italic), ); } + String tokenPrice; + if (store.assets.marketPrices[symbol] != null && balancesInfo != null) { + tokenPrice = (store.assets.marketPrices[symbol] * + Fmt.bigIntToDouble(balancesInfo.total, decimals: decimals)) + .toStringAsFixed(2); + } return RefreshIndicator( key: globalBalanceRefreshKey, @@ -548,17 +555,31 @@ class _AssetsState extends State { 'assets/images/assets/${symbol.isNotEmpty ? symbol : 'DOT'}.png'), ), title: tokenViewTitle, - trailing: Text( - Fmt.priceFloorBigInt( - balancesInfo != null - ? balancesInfo.total - : BigInt.zero, - decimals: decimals, - lengthFixed: 3), - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - color: Colors.black54), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + Fmt.priceFloorBigInt( + balancesInfo != null + ? balancesInfo.total + : BigInt.zero, + decimals: decimals, + lengthFixed: 3), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, + color: Colors.black54), + ), + isPolkadot || isKusama + ? Text( + '≈ \$ ${tokenPrice ?? '--.--'}', + style: TextStyle( + color: Theme.of(context).disabledColor, + ), + ) + : Container(width: 16), + ], ), onTap: () { Navigator.pushNamed(context, AssetPage.route, diff --git a/lib/page/homePage.dart b/lib/page/homePage.dart index 797835ea..f991766c 100644 --- a/lib/page/homePage.dart +++ b/lib/page/homePage.dart @@ -97,9 +97,6 @@ class _HomePageState extends State { return _tabList.asMap().keys.map((i) { if (i == 0) { // return assets page - final bool accountsReady = - store.account.pubKeyAddressMap[store.settings.endpoint.ss58] != - null; return BackgroundWrapper( AssetImage("assets/images/assets/top_bg_$imageColor.png"), Scaffold( @@ -112,10 +109,8 @@ class _HomePageState extends State { actions: [ IconButton( icon: Icon(Icons.menu), - onPressed: accountsReady - ? () => Navigator.of(context) - .pushNamed(NetworkSelectPage.route) - : null, + onPressed: () => + Navigator.of(context).pushNamed(NetworkSelectPage.route), ), ], ), diff --git a/lib/page/networkSelectPage.dart b/lib/page/networkSelectPage.dart index cad2e3fe..7d547ab3 100644 --- a/lib/page/networkSelectPage.dart +++ b/lib/page/networkSelectPage.dart @@ -161,8 +161,11 @@ class _NetworkSelectPageState extends State { accounts.addAll(store.account.optionalAccounts); res.addAll(accounts.map((i) { - String address = - store.account.pubKeyAddressMap[_selectedNetwork.ss58][i.pubKey]; + String address = i.address; + if (store.account.pubKeyAddressMap[_selectedNetwork.ss58] != null) { + address = + store.account.pubKeyAddressMap[_selectedNetwork.ss58][i.pubKey]; + } return RoundedCard( border: address == store.account.currentAddress ? Border.all(color: Theme.of(context).primaryColorLight) diff --git a/lib/service/faucet.dart b/lib/service/faucet.dart index a9ee43b7..d9137b1a 100644 --- a/lib/service/faucet.dart +++ b/lib/service/faucet.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:http/http.dart'; class FaucetApi { - static const String _endpoint = 'https://apps.acala.network/faucet'; + static const String _endpoint = 'https://api.polkawallet.io/faucet'; static Future getAcalaTokens(String address, String deviceId) async { Map headers = {"Content-type": "application/json"}; diff --git a/lib/service/subscan.dart b/lib/service/subscan.dart index 811966bf..aa5f28ee 100644 --- a/lib/service/subscan.dart +++ b/lib/service/subscan.dart @@ -164,4 +164,39 @@ class SubScanApi { } return {}; } + + Future fetchTokenPriceAsync(String network) async { + Completer completer = new Completer(); + ReceivePort receivePort = ReceivePort(); + Isolate isolateIns = await Isolate.spawn( + SubScanApi.fetchTokenPrice, + SubScanRequestParams( + sendPort: receivePort.sendPort, + network: network, + )); + receivePort.listen((msg) { + receivePort.close(); + isolateIns.kill(priority: Isolate.immediate); + completer.complete(msg); + }); + return completer.future; + } + + static Future fetchTokenPrice(SubScanRequestParams para) async { + String url = '${getSnEndpoint(para.network)}/token'; + Map headers = {"Content-type": "application/json"}; + + Response res = await post(url, headers: headers); + if (res.body != null) { + final obj = await compute(jsonDecode, res.body); + if (para.sendPort != null) { + para.sendPort.send(obj['data']); + } + return obj['data']; + } + if (para.sendPort != null) { + para.sendPort.send({}); + } + return {}; + } } diff --git a/lib/service/substrateApi/apiAssets.dart b/lib/service/substrateApi/apiAssets.dart index 432c83c0..ffe31beb 100644 --- a/lib/service/substrateApi/apiAssets.dart +++ b/lib/service/substrateApi/apiAssets.dart @@ -26,6 +26,7 @@ class ApiAssets { if (store.settings.endpoint.info == networkEndpointLaminar.info) { apiRoot.laminar.fetchTokens(store.account.currentAccount.pubKey); } + _fetchMarketPrice(); } Future updateTxs(int page) async { @@ -47,4 +48,18 @@ class ApiAssets { store.assets.setTxsLoading(false); return res; } + + Future _fetchMarketPrice() async { + if (store.settings.endpoint.info == network_name_kusama || + store.settings.endpoint.info == network_name_polkadot) { + final Map res = await webApi.subScanApi + .fetchTokenPriceAsync(store.settings.endpoint.info); + if (res['token'] == null) { + print('fetch market price failed'); + return; + } + final String token = res['token'][0]; + store.assets.setMarketPrices(token, res['detail'][token]['price']); + } + } } diff --git a/lib/store/assets/assets.dart b/lib/store/assets/assets.dart index 5eb2bb42..8f802396 100644 --- a/lib/store/assets/assets.dart +++ b/lib/store/assets/assets.dart @@ -59,6 +59,9 @@ abstract class _AssetsStore with Store { @observable List announcements; + @observable + ObservableMap marketPrices = ObservableMap(); + @computed ObservableList get txsView { return ObservableList.of(txs.where((i) { @@ -212,6 +215,11 @@ abstract class _AssetsStore with Store { announcements = data; } + @action + void setMarketPrices(String token, String price) { + marketPrices[token] = double.parse(price); + } + @action Future loadAccountCache() async { // return if currentAccount not exist diff --git a/lib/store/assets/assets.g.dart b/lib/store/assets/assets.g.dart index f9cc80fe..abc8abdf 100644 --- a/lib/store/assets/assets.g.dart +++ b/lib/store/assets/assets.g.dart @@ -167,6 +167,21 @@ mixin _$AssetsStore on _AssetsStore, Store { }); } + final _$marketPricesAtom = Atom(name: '_AssetsStore.marketPrices'); + + @override + ObservableMap get marketPrices { + _$marketPricesAtom.reportRead(); + return super.marketPrices; + } + + @override + set marketPrices(ObservableMap value) { + _$marketPricesAtom.reportWrite(value, super.marketPrices, () { + super.marketPrices = value; + }); + } + final _$setAccountBalancesAsyncAction = AsyncAction('_AssetsStore.setAccountBalances'); @@ -271,6 +286,17 @@ mixin _$AssetsStore on _AssetsStore, Store { } } + @override + void setMarketPrices(String token, String price) { + final _$actionInfo = _$_AssetsStoreActionController.startAction( + name: '_AssetsStore.setMarketPrices'); + try { + return super.setMarketPrices(token, price); + } finally { + _$_AssetsStoreActionController.endAction(_$actionInfo); + } + } + @override String toString() { return ''' @@ -284,6 +310,7 @@ txs: ${txs}, txsFilter: ${txsFilter}, blockMap: ${blockMap}, announcements: ${announcements}, +marketPrices: ${marketPrices}, txsView: ${txsView} '''; } diff --git a/pubspec.yaml b/pubspec.yaml index 324f9bee..995bbcaf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: PolkaWallet made with Flutter. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.9.3+931 +version: 0.9.4+941 environment: sdk: ">=2.1.0 <3.0.0"