From 563d113a7f2f6615d4cfabcc973f44a027d8e9cb Mon Sep 17 00:00:00 2001 From: shawn Date: Wed, 14 Apr 2021 17:26:52 +0800 Subject: [PATCH 1/5] add karura crowdloan WIP --- lib/app.dart | 31 +- lib/common/consts.dart | 5 +- lib/main-dev.dart | 25 + lib/main-google.dart | 25 + lib/main.dart | 7 +- .../account/import/importAccountPage.dart | 2 +- lib/pages/assets/index.dart | 6 +- lib/pages/homePage.dart | 8 +- lib/pages/profile/aboutPage.dart | 2 +- lib/pages/profile/index.dart | 46 +- lib/pages/public/AdBanner.dart | 297 +++----- lib/pages/{ => public}/guidePage.dart | 0 lib/pages/public/karCrowdLoanFormPage.dart | 487 +++++++++++++ lib/pages/public/karCrowdLoanPage.dart | 673 ++++++++++++++++++ lib/pages/public/karPreAuctionPage.dart | 307 -------- lib/service/apiAccount.dart | 17 +- lib/service/index.dart | 4 +- lib/service/walletApi.dart | 74 +- lib/utils/UI.dart | 26 +- lib/utils/i18n/en/account.dart | 2 +- lib/utils/i18n/en/public.dart | 30 + lib/utils/i18n/index.dart | 4 + lib/utils/i18n/zh/account.dart | 2 +- lib/utils/i18n/zh/public.dart | 29 + pubspec.lock | 26 +- pubspec.yaml | 34 +- 26 files changed, 1575 insertions(+), 594 deletions(-) create mode 100644 lib/main-dev.dart create mode 100644 lib/main-google.dart rename lib/pages/{ => public}/guidePage.dart (100%) create mode 100644 lib/pages/public/karCrowdLoanFormPage.dart create mode 100644 lib/pages/public/karCrowdLoanPage.dart delete mode 100644 lib/pages/public/karPreAuctionPage.dart create mode 100644 lib/utils/i18n/en/public.dart create mode 100644 lib/utils/i18n/zh/public.dart diff --git a/lib/app.dart b/lib/app.dart index 0221a9df..54c293c3 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -7,7 +7,7 @@ import 'package:app/pages/account/import/importAccountPage.dart'; import 'package:app/pages/assets/asset/assetPage.dart'; import 'package:app/pages/assets/transfer/detailPage.dart'; import 'package:app/pages/assets/transfer/transferPage.dart'; -import 'package:app/pages/guidePage.dart'; +import 'package:app/pages/public/guidePage.dart'; import 'package:app/pages/homePage.dart'; import 'package:app/pages/networkSelectPage.dart'; import 'package:app/pages/profile/aboutPage.dart'; @@ -28,7 +28,8 @@ import 'package:app/pages/profile/recovery/vouchRecoveryPage.dart'; import 'package:app/pages/profile/settings/remoteNodeListPage.dart'; import 'package:app/pages/profile/settings/settingsPage.dart'; import 'package:app/pages/profile/account/signPage.dart'; -import 'package:app/pages/public/karPreAuctionPage.dart'; +import 'package:app/pages/public/karCrowdLoanFormPage.dart'; +import 'package:app/pages/public/karCrowdLoanPage.dart'; import 'package:app/pages/walletConnect/walletConnectSignPage.dart'; import 'package:app/pages/walletConnect/wcPairingConfirmPage.dart'; import 'package:app/pages/walletConnect/wcSessionsPage.dart'; @@ -58,8 +59,9 @@ import 'package:polkawallet_ui/pages/txConfirmPage.dart'; const get_storage_container = 'configuration'; class WalletApp extends StatefulWidget { - WalletApp(this.plugins); + WalletApp(this.plugins, this.buildTarget); final List plugins; + final BuildTargets buildTarget; @override _WalletAppState createState() => _WalletAppState(); } @@ -202,7 +204,7 @@ class _WalletAppState extends State { : null, ); - final service = AppService(network, _keyring, _store); + final service = AppService(network, _keyring, _store, widget.buildTarget); service.init(); setState(() { _service = service; @@ -213,6 +215,12 @@ class _WalletAppState extends State { _service.assets.fetchMarketPrice(); } + Future _changeToKusamaForKar() async { + await _changeNetwork( + widget.plugins.firstWhere((e) => e.basic.name == 'kusama')); + _service.store.assets.loadCache(_keyring.current, 'kusama'); + } + Future _changeNode(NetworkParams node) async { if (_connectedNode != null) { setState(() { @@ -228,7 +236,7 @@ class _WalletAppState extends State { Future _checkUpdate(BuildContext context) async { final versions = await WalletApi.getLatestVersion(); - AppUI.checkUpdate(context, versions, autoCheck: true); + AppUI.checkUpdate(context, versions, widget.buildTarget, autoCheck: true); } Future _checkJSCodeUpdate( @@ -283,7 +291,10 @@ class _WalletAppState extends State { final pluginIndex = widget.plugins .indexWhere((e) => e.basic.name == store.settings.network); final service = AppService( - widget.plugins[pluginIndex > -1 ? pluginIndex : 0], _keyring, store); + widget.plugins[pluginIndex > -1 ? pluginIndex : 0], + _keyring, + store, + widget.buildTarget); service.init(); setState(() { _store = store; @@ -344,8 +355,8 @@ class _WalletAppState extends State { builder: (_, AsyncSnapshot snapshot) { if (snapshot.hasData && _service != null) { return snapshot.data > 0 - ? HomePage( - _service, _connectedNode, _checkJSCodeUpdate) + ? HomePage(_service, _connectedNode, + _checkJSCodeUpdate, _changeToKusamaForKar) : CreateAccountEntryPage(); } else { return Container(color: Theme.of(context).canvasColor); @@ -370,7 +381,9 @@ class _WalletAppState extends State { WalletConnectSignPage.route: (_) => WalletConnectSignPage(_service, _service.account.getPassword), GuidePage.route: (_) => GuidePage(), - KarPreAuctionPage.route: (_) => KarPreAuctionPage(_service), + KarCrowdLoanPage.route: (_) => KarCrowdLoanPage(_service, _connectedNode), + KarCrowdLoanFormPage.route: (_) => + KarCrowdLoanFormPage(_service, _connectedNode), /// account CreateAccountEntryPage.route: (_) => CreateAccountEntryPage(), diff --git a/lib/common/consts.dart b/lib/common/consts.dart index 2184c4ee..bb48d4ca 100644 --- a/lib/common/consts.dart +++ b/lib/common/consts.dart @@ -14,8 +14,9 @@ const prefixList = [ ]; /// app versions -const String app_beta_version = 'v2.0.2-beta.2'; -const int app_beta_version_code = 2022; +enum BuildTargets { apk, playStore, dev } +const String app_beta_version = 'v2.0.2-beta.3'; +const int app_beta_version_code = 2023; const show_guide_status_key = 'show_guide_status'; const show_banner_status_key = 'show_banner_status'; diff --git a/lib/main-dev.dart b/lib/main-dev.dart new file mode 100644 index 00000000..0a8dab48 --- /dev/null +++ b/lib/main-dev.dart @@ -0,0 +1,25 @@ +import 'package:app/app.dart'; +import 'package:app/common/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:polkawallet_plugin_kusama/polkawallet_plugin_kusama.dart'; +import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; +import 'package:polkawallet_plugin_laminar/polkawallet_plugin_laminar.dart'; +import 'package:polkawallet_plugin_chainx/polkawallet_plugin_chainx.dart'; +// import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; + +import 'package:get_storage/get_storage.dart'; + +void main() async { + await GetStorage.init(get_storage_container); + + final _plugins = [ + PluginKusama(name: 'polkadot'), + PluginKusama(), + PluginAcala(), + PluginLaminar(), + PluginChainX(), + // PluginEdgeware(), + ]; + + runApp(WalletApp(_plugins, BuildTargets.dev)); +} diff --git a/lib/main-google.dart b/lib/main-google.dart new file mode 100644 index 00000000..cbc24d1a --- /dev/null +++ b/lib/main-google.dart @@ -0,0 +1,25 @@ +import 'package:app/app.dart'; +import 'package:app/common/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:polkawallet_plugin_kusama/polkawallet_plugin_kusama.dart'; +import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; +import 'package:polkawallet_plugin_laminar/polkawallet_plugin_laminar.dart'; +import 'package:polkawallet_plugin_chainx/polkawallet_plugin_chainx.dart'; +// import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; + +import 'package:get_storage/get_storage.dart'; + +void main() async { + await GetStorage.init(get_storage_container); + + final _plugins = [ + PluginKusama(name: 'polkadot'), + PluginKusama(), + PluginAcala(), + PluginLaminar(), + PluginChainX(), + // PluginEdgeware(), + ]; + + runApp(WalletApp(_plugins, BuildTargets.playStore)); +} diff --git a/lib/main.dart b/lib/main.dart index 14307e4b..dd77039f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,13 @@ import 'package:app/app.dart'; +import 'package:app/common/consts.dart'; import 'package:flutter/material.dart'; import 'package:polkawallet_plugin_kusama/polkawallet_plugin_kusama.dart'; import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; -// import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; +import 'package:polkawallet_plugin_laminar/polkawallet_plugin_laminar.dart'; import 'package:polkawallet_plugin_chainx/polkawallet_plugin_chainx.dart'; +// import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; import 'package:get_storage/get_storage.dart'; -import 'package:polkawallet_plugin_laminar/polkawallet_plugin_laminar.dart'; void main() async { await GetStorage.init(get_storage_container); @@ -20,5 +21,5 @@ void main() async { // PluginEdgeware(), ]; - runApp(WalletApp(_plugins)); + runApp(WalletApp(_plugins, BuildTargets.apk)); } diff --git a/lib/pages/account/import/importAccountPage.dart b/lib/pages/account/import/importAccountPage.dart index 4bc30a72..7f74c536 100644 --- a/lib/pages/account/import/importAccountPage.dart +++ b/lib/pages/account/import/importAccountPage.dart @@ -100,7 +100,7 @@ class _ImportAccountPageState extends State { setState(() { _submitting = false; }); - if (err.toString().contains('unreachable')) { + if (err.toString().contains('Invalid')) { await showCupertinoDialog( context: context, builder: (BuildContext context) { diff --git a/lib/pages/assets/index.dart b/lib/pages/assets/index.dart index e9178fbb..7c9951ef 100644 --- a/lib/pages/assets/index.dart +++ b/lib/pages/assets/index.dart @@ -32,12 +32,14 @@ class AssetsPage extends StatefulWidget { this.service, this.connectedNode, this.checkJSCodeUpdate, + this.changeToKusama, this.handleWalletConnect, ); final AppService service; final NetworkParams connectedNode; final Future Function(PolkawalletPlugin) checkJSCodeUpdate; + final Future Function() changeToKusama; final Future Function(String) handleWalletConnect; @override @@ -444,7 +446,9 @@ class _AssetsState extends State { Expanded(child: Container()), widget.service.store.account.showBanner && !(widget.service.keyring.current.observation ?? false) - ? AdBanner(widget.service, canClose: true) + ? AdBanner(widget.service, widget.connectedNode, + widget.changeToKusama, + canClose: true) : Container(), ], ) diff --git a/lib/pages/homePage.dart b/lib/pages/homePage.dart index 717d02f7..c53be777 100644 --- a/lib/pages/homePage.dart +++ b/lib/pages/homePage.dart @@ -14,12 +14,14 @@ import 'package:polkawallet_sdk/utils/i18n.dart'; import 'package:polkawallet_ui/utils/i18n.dart'; class HomePage extends StatefulWidget { - HomePage(this.service, this.connectedNode, this.checkJSCodeUpdate); + HomePage(this.service, this.connectedNode, this.checkJSCodeUpdate, + this.changeToKusama); final AppService service; final NetworkParams connectedNode; final Future Function(BuildContext, PolkawalletPlugin, {bool needReload}) checkJSCodeUpdate; + final Future Function() changeToKusama; static final String route = '/'; @@ -81,6 +83,7 @@ class _HomePageState extends State { widget.connectedNode, (PolkawalletPlugin plugin) => widget.checkJSCodeUpdate(context, plugin), + widget.changeToKusama, _handleWalletConnect), // content: Container(), ) @@ -97,7 +100,8 @@ class _HomePageState extends State { 'assets/images/nav_profile.svg', color: Theme.of(context).primaryColor, ), - content: ProfilePage(widget.service, widget.connectedNode), + content: ProfilePage( + widget.service, widget.connectedNode, widget.changeToKusama), )); return Scaffold( body: Stack( diff --git a/lib/pages/profile/aboutPage.dart b/lib/pages/profile/aboutPage.dart index bbdee51e..fce46132 100644 --- a/lib/pages/profile/aboutPage.dart +++ b/lib/pages/profile/aboutPage.dart @@ -31,7 +31,7 @@ class _AboutPage extends State { setState(() { _loading = false; }); - AppUI.checkUpdate(context, versions); + AppUI.checkUpdate(context, versions, widget.service.buildTarget); } @override diff --git a/lib/pages/profile/index.dart b/lib/pages/profile/index.dart index f31d4499..f9b33cb0 100644 --- a/lib/pages/profile/index.dart +++ b/lib/pages/profile/index.dart @@ -20,10 +20,11 @@ import 'package:polkawallet_ui/utils/i18n.dart'; import 'package:polkawallet_ui/utils/index.dart'; class ProfilePage extends StatefulWidget { - ProfilePage(this.service, this.connectedNode); + ProfilePage(this.service, this.connectedNode, this.changeToKusama); final AppService service; final NetworkParams connectedNode; + final Future Function() changeToKusama; @override _ProfilePageState createState() => _ProfilePageState(); @@ -98,27 +99,32 @@ class _ProfilePageState extends State { ), ), ), - !(acc.observation ?? false) ? AdBanner(widget.service) : Container(), !(acc.observation ?? false) - ? Container( - padding: EdgeInsets.all(24), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - RoundedButton( - text: dic['account'], - onPressed: () async { - await Navigator.pushNamed( - context, AccountManagePage.route); - setState(() { - _currentAccount = widget.service.keyring.current; - }); - }, - ) - ], - ), + ? AdBanner( + widget.service, widget.connectedNode, widget.changeToKusama) + : Container(), + Container( + padding: EdgeInsets.all(24), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RoundedButton( + text: dic['account'], + onPressed: () async { + if (acc.observation ?? false) { + await Navigator.pushNamed(context, ContactsPage.route); + } else { + await Navigator.pushNamed( + context, AccountManagePage.route); + } + setState(() { + _currentAccount = widget.service.keyring.current; + }); + }, ) - : Container(height: 24), + ], + ), + ), ListTile( leading: Container( width: 32, diff --git a/lib/pages/public/AdBanner.dart b/lib/pages/public/AdBanner.dart index 0d1aff98..7ce63f84 100644 --- a/lib/pages/public/AdBanner.dart +++ b/lib/pages/public/AdBanner.dart @@ -1,171 +1,59 @@ import 'dart:async'; import 'package:app/common/consts.dart'; -import 'package:app/pages/public/karPreAuctionPage.dart'; +import 'package:app/main.dart'; +import 'package:app/pages/public/karCrowdLoanPage.dart'; +import 'package:app/service/walletApi.dart'; +import 'package:app/utils/i18n/index.dart'; +import 'package:polkawallet_sdk/api/types/networkParams.dart'; +import 'package:polkawallet_sdk/utils/i18n.dart'; import 'package:app/service/index.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class AdBanner extends StatelessWidget { - AdBanner(this.service, {this.canClose = false}); +class AdBanner extends StatefulWidget { + AdBanner(this.service, this.connectedNode, this.changeToKusama, + {this.canClose = false}); + final AppService service; + final NetworkParams connectedNode; final bool canClose; + final Future Function() changeToKusama; @override - Widget build(BuildContext context) { - final endTime = DateTime.utc(2021, 4, 11, 0, 0, 0); - final now = DateTime.now().millisecondsSinceEpoch; - final show = now < endTime.millisecondsSinceEpoch; - - final fullWidth = MediaQuery.of(context).size.width; - final cardColor = Theme.of(context).cardColor; - return Container( - child: show - ? Stack( - alignment: AlignmentDirectional.topEnd, - children: [ - GestureDetector( - child: Stack( - children: [ - Row( - children: [ - Expanded( - child: Container( - margin: EdgeInsets.all(8), - child: Image.asset( - 'assets/images/public/banner_kar_plo.png'), - )) - ], - ), - Container( - constraints: BoxConstraints(maxHeight: fullWidth / 4), - child: Column( - children: [ - Row( - children: [ - Container( - margin: EdgeInsets.only( - left: 20, top: 20, bottom: 10), - child: CountdownPanel( - cardColor: Theme.of(context).cardColor, - cardTextColor: Colors.pink, - textColor: Colors.orangeAccent, - endTime: endTime, - ), - ) - ], - ), - Row( - children: [ - Container( - width: fullWidth / 3 + 16, - margin: EdgeInsets.only(left: 24, right: 8), - child: Image.asset( - 'assets/images/public/kar_logo.png'), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Parachain Auction', - style: TextStyle( - color: cardColor, - height: 0.9, - fontSize: 14), - ), - Text( - 'Pre-support', - style: TextStyle( - color: cardColor, - height: 0.9, - fontSize: 14), - ), - ], - ) - ], - ) - ], - ), - ) - ], - ), - onTap: () => - Navigator.of(context).pushNamed(KarPreAuctionPage.route), - ), - canClose - ? Container( - padding: EdgeInsets.only(top: 12, right: 12), - child: GestureDetector( - child: Icon( - Icons.cancel, - color: Colors.white60, - size: 16, - ), - onTap: () { - service.store.storage - .write(show_banner_status_key, 'closed'); - service.store.account.setBannerVisible(false); - }, - ), - ) - : Container() - ], - ) - : null, - ); - } -} - -class CountdownPanel extends StatefulWidget { - CountdownPanel({ - this.cardColor, - this.cardTextColor, - this.textColor, - this.endTime, - }); - - final Color cardColor; - final Color cardTextColor; - final Color textColor; - final DateTime endTime; - - @override - _CountdownPanel createState() => _CountdownPanel(); + _AdBannerState createState() => _AdBannerState(); } -class _CountdownPanel extends State { - Timer _timer; +class _AdBannerState extends State { + bool _started = false; - void _updateTime() { - setState(() { - _timer = Timer(Duration(seconds: 1), _updateTime); - }); + Future _getCrowdLoanStarted() async { + final res = await WalletApi.getKarCrowdLoanStarted(); + if (res != null && res['result'] && mounted) { + setState(() { + _started = true; + }); + } } - Widget _buildCard(String text) { - return Container( - margin: EdgeInsets.only(left: 4, right: 2), - padding: EdgeInsets.only(left: 6, right: 6), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(4)), - color: widget.cardColor, - ), - constraints: BoxConstraints(minWidth: 24), - child: Text( - text, - textAlign: TextAlign.center, - style: TextStyle( - color: widget.cardTextColor, - fontWeight: FontWeight.w700, - fontSize: 22, - fontFamily: 'BebasNeue'), - ), - ); - } + Future _goToCrowdLoan(BuildContext context) async { + final dic = I18n.of(context).getDic(i18n_full_dic_app, 'public'); + + if (widget.service.plugin.basic.name != 'kusama') { + showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(dic['auction.switch']), + content: Container(height: 64, child: CupertinoActivityIndicator()), + ); + }, + ); + await widget.changeToKusama(); + Navigator.of(context).pop(); + } - String formatTime(int num) { - final str = num.toString(); - return str.length == 1 ? '0$str' : str; + Navigator.of(context).pushNamed(KarCrowdLoanPage.route); } @override @@ -173,41 +61,86 @@ class _CountdownPanel extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - _updateTime(); + _getCrowdLoanStarted(); }); } - @override - void dispose() { - super.dispose(); - if (_timer != null) { - _timer.cancel(); - } - } - @override Widget build(BuildContext context) { - final now = DateTime.now(); - final left = widget.endTime.difference(now); - return Container( - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - _buildCard(left.inDays.toString()), - Text( - 'days', - style: - TextStyle(fontWeight: FontWeight.bold, color: widget.textColor), - ), - _buildCard( - '${formatTime(left.inHours % 24)}:${formatTime(left.inMinutes % 60)}:${formatTime(left.inSeconds % 60)}'), - Text( - 'left', - style: - TextStyle(fontWeight: FontWeight.bold, color: widget.textColor), - ), - ], - ), - ); + // todo: only available in dev now + final show = widget.connectedNode != null && + _started && + widget.service.buildTarget == BuildTargets.dev; + + final fullWidth = MediaQuery.of(context).size.width; + final cardColor = Theme.of(context).cardColor; + return !show + ? Container() + : Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + GestureDetector( + child: Stack( + children: [ + Row( + children: [ + Expanded( + child: Container( + margin: EdgeInsets.all(8), + child: Image.asset( + 'assets/images/public/banner_kar_plo.png'), + ), + ) + ], + ), + Container( + constraints: BoxConstraints(maxHeight: fullWidth / 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: fullWidth / 3 + 24, + margin: + EdgeInsets.only(left: 24, top: 28, right: 8), + child: Image.asset( + 'assets/images/public/kar_logo.png'), + ), + Container( + margin: EdgeInsets.only(left: 24, top: 8), + child: Text( + 'Parachain Auction Now Live', + style: TextStyle( + color: cardColor, + height: 0.9, + fontSize: 22, + fontWeight: FontWeight.bold), + ), + ), + ], + ), + ) + ], + ), + onTap: () => _goToCrowdLoan(context), + ), + widget.canClose + ? Container( + padding: EdgeInsets.only(top: 12, right: 12), + child: GestureDetector( + child: Icon( + Icons.cancel, + color: Colors.white60, + size: 16, + ), + onTap: () { + widget.service.store.storage + .write(show_banner_status_key, 'closed'); + widget.service.store.account.setBannerVisible(false); + }, + ), + ) + : Container() + ], + ); } } diff --git a/lib/pages/guidePage.dart b/lib/pages/public/guidePage.dart similarity index 100% rename from lib/pages/guidePage.dart rename to lib/pages/public/guidePage.dart diff --git a/lib/pages/public/karCrowdLoanFormPage.dart b/lib/pages/public/karCrowdLoanFormPage.dart new file mode 100644 index 00000000..f38adb43 --- /dev/null +++ b/lib/pages/public/karCrowdLoanFormPage.dart @@ -0,0 +1,487 @@ +import 'package:app/pages/public/karCrowdLoanPage.dart'; +import 'package:app/service/index.dart'; +import 'package:app/service/walletApi.dart'; +import 'package:app/utils/i18n/index.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:polkawallet_sdk/storage/types/keyPairData.dart'; +import 'package:polkawallet_sdk/api/types/networkParams.dart'; +import 'package:polkawallet_sdk/utils/i18n.dart'; +import 'package:polkawallet_ui/components/addressIcon.dart'; +import 'package:polkawallet_ui/components/roundedButton.dart'; +import 'package:polkawallet_ui/components/txButton.dart'; +import 'package:polkawallet_ui/components/tapTooltip.dart'; +import 'package:polkawallet_ui/pages/txConfirmPage.dart'; +import 'package:polkawallet_ui/utils/format.dart'; +import 'package:polkawallet_ui/utils/index.dart'; + +class KarCrowdLoanFormPage extends StatefulWidget { + KarCrowdLoanFormPage(this.service, this.connectedNode); + final AppService service; + final NetworkParams connectedNode; + + static final String route = '/public/kar/auction/2'; + + @override + _KarCrowdLoanFormPageState createState() => _KarCrowdLoanFormPageState(); +} + +const kar_para_index = '1000'; + +class _KarCrowdLoanFormPageState extends State { + final _emailRegEx = RegExp( + r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"); + final _referralRegEx = RegExp(r'^0x[0-9a-z]{64}$'); + final _emailFocusNode = FocusNode(); + final _amountFocusNode = FocusNode(); + final _referralFocusNode = FocusNode(); + + bool _submitting = false; + + String _email = ''; + double _amount = 0; + String _referral = ''; + bool _emailValid = false; + bool _amountValid = false; + bool _referralValid = false; + + double _amountKar = 0; + + bool _emailAccept = true; + + void _onEmailChange(String value) { + final v = value.trim(); + if (v.isEmpty) { + setState(() { + _email = v; + _emailValid = false; + }); + return; + } + + final valid = _emailRegEx.hasMatch(v); + setState(() { + _emailValid = valid; + _email = v; + }); + } + + void _onAmountChange(String value, BigInt balanceInt) { + final v = value.trim(); + if (v.isEmpty) { + setState(() { + _amount = 0; + _amountValid = false; + _amountKar = 0; + }); + return; + } + + final amt = double.parse(v); + + final decimals = 12; + // final decimals = widget.service.plugin.networkState.tokenDecimals[0]; + final valid = amt < Fmt.bigIntToDouble(balanceInt, decimals); + setState(() { + _amountValid = valid; + _amount = amt; + _amountKar = valid ? amt * 12 : 0; + }); + } + + Future _onReferralChange(String value) async { + final v = value.trim(); + if (v.isEmpty) { + setState(() { + _referral = v; + _referralValid = false; + }); + return; + } + + final valid = _referralRegEx.hasMatch(v); + if (!valid) { + setState(() { + _referral = v; + _referralValid = valid; + }); + return; + } + final res = await WalletApi.verifyKarReferralCode(v); + print(res); + // todo: valid2 = true for testing + final valid2 = true; + // final valid2 = res != null && res['result']; + setState(() { + _referral = v; + _referralValid = valid2; + }); + } + + Future _signAndSubmit(KeyPairData account) async { + if (_submitting || + widget.connectedNode == null || + !(_amountValid && _emailValid && (_referralValid || _referral.isEmpty))) + return; + + setState(() { + _submitting = true; + }); + final decimals = 12; + // final decimals = widget.service.plugin.networkState.tokenDecimals[0]; + final signed = widget.service.store.storage + .read('$kar_statement_store_key${account.pubKey}'); + final amountInt = Fmt.tokenInt(_amount.toString(), decimals); + // todo: add this post request while API is ready. + // final res = await WalletApi.postKarCrowdLoan( + // account.address, amountInt, _email, _referral, signed); + // print(res); + final res = {'result': true}; + if (res != null && res['result']) { + final dic = I18n.of(context).getDic(i18n_full_dic_app, 'public'); + // todo: use response data while API is ready. + // final signingPayload = {'Sr25519': res['signingPayload']}; + final signingPayload = {'Sr25519': signed}; + final res = (await Navigator.of(context).pushNamed(TxConfirmPage.route, + arguments: TxConfirmParams( + module: 'crowdloan', + call: 'contribute', + txTitle: dic['auction.contribute'], + txDisplay: { + "paraIndex": kar_para_index, + "amount": '$_amount KSM', + "signingPayload": signingPayload + }, + params: [kar_para_index, amountInt.toString(), signingPayload], + ))) as Map; + if (res != null) { + if (_emailAccept) { + // todo: remove this await in production + final resTest = await WalletApi.postKarSubscribe(_email); + print(resTest); + } + await showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + content: Text('Success'), + actions: [ + CupertinoButton( + child: Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ); + }, + ); + Navigator.of(context).pop(); + } + + setState(() { + _submitting = false; + }); + } else { + showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text('Failed'), + content: Text('Get Karura crowdloan info failed.'), + actions: [ + CupertinoButton( + child: Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ); + }, + ); + setState(() { + _submitting = false; + }); + } + } + + @override + void dispose() { + super.dispose(); + _emailFocusNode.dispose(); + _referralFocusNode.dispose(); + _amountFocusNode.dispose(); + } + + @override + Widget build(_) { + return Observer(builder: (BuildContext context) { + final dic = I18n.of(context).getDic(i18n_full_dic_app, 'public'); + final decimals = 12; + // final decimals = widget.service.plugin.networkState.tokenDecimals[0]; + + final cardColor = Theme.of(context).cardColor; + final karColor = Colors.red; + final grayColor = Colors.white70; + final errorStyle = TextStyle(color: karColor, fontSize: 10); + final karStyle = TextStyle( + color: cardColor, fontSize: 32, fontWeight: FontWeight.bold); + final karKeyStyle = TextStyle(color: cardColor); + final karInfoStyle = + TextStyle(color: karColor, fontSize: 20, fontWeight: FontWeight.bold); + + final KeyPairData account = ModalRoute.of(context).settings.arguments; + final balanceInt = Fmt.balanceInt( + widget.service.plugin.balances.native.availableBalance.toString()); + final balanceView = + Fmt.priceFloorBigInt(balanceInt, decimals, lengthMax: 8); + + final inputValid = + _emailValid && _amountValid && (_referralValid || _referral.isEmpty); + final isConnected = widget.connectedNode != null; + + return CrowdLoanPageLayout(dic['auction.contribute'], [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(top: 16, bottom: 16), + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + decoration: BoxDecoration( + color: Colors.white24, + border: Border.all(color: grayColor), + borderRadius: BorderRadius.all(Radius.circular(64))), + child: Row( + children: [ + AddressIcon( + account.address ?? '', + svg: account.icon, + size: 36, + tapToCopy: false, + ), + Expanded( + child: Container( + margin: EdgeInsets.only(left: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + account.name ?? '', + style: TextStyle(fontSize: 18, color: cardColor), + ), + Text( + Fmt.address(account.address ?? ''), + style: TextStyle(color: grayColor, fontSize: 14), + ), + ], + ), + )), + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 8, bottom: 4), + child: CupertinoTextField( + padding: EdgeInsets.all(16), + placeholder: dic['auction.email'], + placeholderStyle: TextStyle(color: grayColor, fontSize: 18), + style: TextStyle(color: cardColor, fontSize: 18), + decoration: BoxDecoration( + color: Colors.white24, + borderRadius: BorderRadius.all(Radius.circular(64)), + border: Border.all( + color: _emailFocusNode.hasFocus ? karColor : grayColor), + ), + cursorColor: karColor, + clearButtonMode: OverlayVisibilityMode.editing, + focusNode: _emailFocusNode, + onChanged: _onEmailChange, + ), + ), + Container( + height: 12, + margin: EdgeInsets.only(left: 16, bottom: 8), + child: _email.isEmpty || _emailValid + ? Container() + : Text( + '${dic['auction.invalid']} ${dic['auction.email']}', + style: errorStyle, + ), + ), + Padding( + padding: EdgeInsets.only(left: 16, bottom: 4), + child: Text( + '${dic['auction.balance']}: $balanceView KSM', + style: TextStyle(color: Colors.white70), + ), + ), + Container( + margin: EdgeInsets.only(bottom: 4), + child: CupertinoTextField( + padding: EdgeInsets.all(16), + placeholder: dic['auction.amount'], + placeholderStyle: TextStyle(color: grayColor, fontSize: 18), + style: TextStyle(color: cardColor, fontSize: 18), + decoration: BoxDecoration( + color: Colors.white24, + borderRadius: BorderRadius.all(Radius.circular(64)), + border: Border.all( + color: _amountFocusNode.hasFocus ? karColor : grayColor), + ), + cursorColor: karColor, + clearButtonMode: OverlayVisibilityMode.editing, + inputFormatters: [UI.decimalInputFormatter(decimals)], + focusNode: _amountFocusNode, + onChanged: (v) => _onAmountChange(v, balanceInt), + ), + ), + Container( + height: 12, + margin: EdgeInsets.only(left: 16, bottom: 8), + child: _amount == 0 || _amountValid + ? Container() + : Text( + '${dic['auction.invalid']} ${dic['auction.amount']}', + style: errorStyle, + ), + ), + Container( + margin: EdgeInsets.only(bottom: 4), + child: CupertinoTextField( + padding: EdgeInsets.all(16), + placeholder: dic['auction.referral'], + placeholderStyle: TextStyle(color: grayColor, fontSize: 18), + style: TextStyle(color: cardColor, fontSize: 18), + decoration: BoxDecoration( + color: Colors.white24, + borderRadius: BorderRadius.all(Radius.circular(64)), + border: Border.all( + color: + _referralFocusNode.hasFocus ? karColor : grayColor), + ), + cursorColor: karColor, + clearButtonMode: OverlayVisibilityMode.editing, + focusNode: _referralFocusNode, + onChanged: (v) => _onReferralChange(v), + ), + ), + Container( + height: 12, + margin: EdgeInsets.only(left: 16, bottom: 8), + child: _referral.isEmpty || _referralValid + ? Container() + : Text( + '${dic['auction.invalid']} ${dic['auction.referral']}', + style: errorStyle, + ), + ), + Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white30, + border: Border.all(color: grayColor), + borderRadius: BorderRadius.all(Radius.circular(24))), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text(dic['auction.estimate'], style: karKeyStyle), + TapTooltip( + message: dic['auction.note'], + ) + ], + ), + Text('${Fmt.priceFloor(_amountKar)} KAR', style: karStyle), + _referralValid + ? Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text('+ ${Fmt.priceFloor(_amountKar * 0.05)} KAR', + style: TextStyle( + color: cardColor, + fontSize: 26, + fontWeight: FontWeight.bold)), + Expanded( + child: Text(' (+5%)', style: karInfoStyle)), + ], + ) + : Container(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + dic['auction.init'], + style: karKeyStyle, + ), + Text('30%', style: karInfoStyle), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(dic['auction.vest'], style: karKeyStyle), + Text('70%', style: karInfoStyle), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(dic['auction.lease'], style: karKeyStyle), + Text('12 MONTHS', style: karInfoStyle), + ], + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Theme( + child: SizedBox( + height: 48, + width: 32, + child: Padding( + padding: EdgeInsets.only(right: 8), + child: Checkbox( + value: _emailAccept, + onChanged: (v) { + setState(() { + _emailAccept = v; + }); + }, + ), + ), + ), + data: ThemeData( + primarySwatch: karColor, + unselectedWidgetColor: karColor, // Your color + ), + ), + Expanded( + child: Text( + dic['auction.notify'], + style: TextStyle(color: cardColor, fontSize: 10), + )) + ], + ), + Container( + margin: EdgeInsets.only(top: 4, bottom: 32), + child: RoundedButton( + text: isConnected + ? dic['auction.submit'] + : dic['auction.connecting'], + icon: _submitting || !isConnected + ? CupertinoActivityIndicator() + : null, + color: inputValid && !_submitting && isConnected + ? karColor + : Colors.grey, + onPressed: () => _signAndSubmit(account), + ), + ) + ], + ) + ]); + }); + } +} diff --git a/lib/pages/public/karCrowdLoanPage.dart b/lib/pages/public/karCrowdLoanPage.dart new file mode 100644 index 00000000..ba0f11e8 --- /dev/null +++ b/lib/pages/public/karCrowdLoanPage.dart @@ -0,0 +1,673 @@ +import 'dart:async'; + +import 'package:app/pages/public/karCrowdLoanFormPage.dart'; +import 'package:app/service/index.dart'; +import 'package:app/service/walletApi.dart'; +import 'package:app/utils/i18n/index.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:polkawallet_sdk/storage/types/keyPairData.dart'; +import 'package:polkawallet_sdk/webviewWithExtension/types/signExtrinsicParam.dart'; +import 'package:polkawallet_sdk/api/types/networkParams.dart'; +import 'package:polkawallet_sdk/utils/i18n.dart'; +import 'package:polkawallet_ui/components/addressIcon.dart'; +import 'package:polkawallet_ui/components/roundedButton.dart'; +import 'package:polkawallet_ui/pages/accountListPage.dart'; +import 'package:polkawallet_ui/utils/format.dart'; +import 'package:polkawallet_ui/utils/index.dart'; + +const kar_statement_store_key = 'kar_statement_store_key'; + +class KarCrowdLoanPage extends StatefulWidget { + KarCrowdLoanPage(this.service, this.connectedNode); + final AppService service; + final NetworkParams connectedNode; + + static final String route = '/public/kar/auction'; + + @override + _KarCrowdLoanPageState createState() => _KarCrowdLoanPageState(); +} + +class _KarCrowdLoanPageState extends State { + int _bestNumber = 0; + Map _fundInfo; + + KeyPairData _account = KeyPairData(); + + bool _submitting = false; + bool _accepted0 = false; + bool _accepted1 = false; + bool _accepted2 = false; + + String _statement; + bool _signed = false; + + Future _updateBestNumber() async { + final res = await widget.service.plugin.sdk.webView + .evalJavascript('api.derive.chain.bestNumber()'); + if (mounted) { + setState(() { + _bestNumber = int.parse(res.toString()); + }); + } + } + + Future _getCrowdLoanInfo() async { + if (widget.connectedNode == null) return; + + _updateBestNumber(); + final res = await widget.service.plugin.sdk.webView + .evalJavascript('api.query.crowdloan.funds("$kar_para_index")'); + if (mounted) { + setState(() { + _fundInfo = res; + }); + } + } + + Future _getKarStatement() async { + final res = await WalletApi.getKarCrowdLoanStatement(); + print(res); + if (res != null && mounted) { + setState(() { + _statement = res['statement']; + }); + } + } + + Future _selectAccount() async { + final res = await Navigator.of(context).pushNamed(AccountListPage.route, + arguments: AccountListPageParams( + list: widget.service.keyring.keyPairs, title: 'Accounts')); + if (res != null) { + final acc = res as KeyPairData; + if (acc.pubKey == _account.pubKey) return; + + final signed = widget.service.store.storage + .read('$kar_statement_store_key${acc.pubKey}'); + + setState(() { + _account = acc; + _accepted0 = false; + _accepted1 = false; + _accepted2 = false; + _signed = signed != null; + }); + } + } + + Future _acceptAndSign() async { + if (_submitting) return; + + setState(() { + _submitting = true; + }); + final password = + await widget.service.account.getPassword(context, _account); + + if (password != null) { + final params = SignAsExtensionParam(); + params.msgType = "pub(bytes.sign)"; + params.request = { + "address": _account.address, + "data": _statement, + }; + + final signRes = await widget.service.plugin.sdk.api.keyring + .signAsExtension(password, params); + widget.service.store.storage.write( + '$kar_statement_store_key${_account.pubKey}', signRes.signature); + + setState(() { + _submitting = false; + _signed = true; + }); + + Navigator.of(context) + .pushNamed(KarCrowdLoanFormPage.route, arguments: _account); + } else { + setState(() { + _submitting = false; + }); + } + } + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _getCrowdLoanInfo(); + _getKarStatement(); + + final acc = widget.service.keyring.current; + final signed = widget.service.store.storage + .read('$kar_statement_store_key${acc.pubKey}'); + + setState(() { + _account = widget.service.keyring.current; + _signed = signed != null; + }); + }); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + if (widget.connectedNode != null && _fundInfo == null) { + _getCrowdLoanInfo(); + _getKarStatement(); + } + } + + @override + Widget build(BuildContext context) { + final dic = I18n.of(context).getDic(i18n_full_dic_app, 'public'); + + DateTime endTime = DateTime.now(); + bool finished = false; + if (_fundInfo != null) { + final end = _fundInfo['end']; + + final now = DateTime.now().millisecondsSinceEpoch; + final blockDuration = int.parse( + widget.service.plugin.networkConst['babe']['expectedBlockTime']); + endTime = DateTime.fromMillisecondsSinceEpoch( + now + (end - _bestNumber) * blockDuration); + + finished = now > endTime.millisecondsSinceEpoch; + } + + final cardColor = Theme.of(context).cardColor; + final karColor = Colors.red; + final grayColor = Colors.white70; + + final allAccepted = _accepted0 && _accepted1 && _accepted2; + return CrowdLoanPageLayout('', [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(bottom: 8, top: 16), + child: Text(dic['auction.support'], + style: TextStyle(fontSize: 24, color: cardColor)), + ), + Row( + children: [ + Image.asset( + 'assets/images/public/kar_logo.png', + width: MediaQuery.of(context).size.width * 2 / 3, + ) + ], + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text(dic['auction.kar'], + style: TextStyle( + fontSize: 28, + color: karColor, + fontWeight: FontWeight.bold)), + ), + Container( + margin: EdgeInsets.only(top: 20), + child: Stack( + children: [ + Padding( + padding: EdgeInsets.only(left: 4, top: 2), + child: Row( + children: [ + Expanded( + child: FittedBox( + child: Text( + dic['auction.${finished ? 'finish' : 'live'}'] + .toUpperCase(), + style: TextStyle( + color: karColor, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic)))) + ], + ), + ), + Padding( + padding: EdgeInsets.only(right: 4, bottom: 2), + child: Row( + children: [ + Expanded( + child: FittedBox( + child: Text( + dic['auction.${finished ? 'finish' : 'live'}'] + .toUpperCase(), + style: TextStyle( + color: cardColor, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic)))) + ], + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 8, bottom: 32), + child: widget.connectedNode == null || _fundInfo == null || finished + ? null + : CountdownPanel( + cardColor: Theme.of(context).cardColor, + cardTextColor: Colors.pink, + textColor: Colors.orangeAccent, + endTime: endTime, + ), + ) + ], + ), + _fundInfo == null || finished + ? _bestNumber == 0 + ? Theme( + data: ThemeData( + cupertinoOverrideTheme: + CupertinoThemeData(brightness: Brightness.dark)), + child: CupertinoActivityIndicator()) + : Container() + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(bottom: 4), + child: Divider(color: grayColor), + ), + Text( + dic['auction.address'], + style: TextStyle(color: grayColor, fontSize: 18), + ), + GestureDetector( + child: Container( + margin: EdgeInsets.only(top: 8, bottom: 16), + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + decoration: BoxDecoration( + border: Border.all(color: grayColor), + borderRadius: BorderRadius.all(Radius.circular(64))), + child: Row( + children: [ + AddressIcon( + _account.address ?? '', + svg: _account.icon, + size: 36, + tapToCopy: false, + ), + Expanded( + child: Container( + margin: EdgeInsets.only(left: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _account.name ?? '', + style: + TextStyle(fontSize: 18, color: cardColor), + ), + Text( + Fmt.address(_account.address ?? ''), + style: + TextStyle(color: grayColor, fontSize: 14), + ), + ], + ), + )), + Icon( + Icons.keyboard_arrow_down, + size: 30, + color: grayColor, + ) + ], + ), + ), + onTap: _selectAccount, + ), + _signed + ? Container() + : Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Theme( + child: SizedBox( + height: 48, + width: 32, + child: Padding( + padding: EdgeInsets.only(right: 8), + child: Checkbox( + value: _accepted0, + onChanged: (v) { + setState(() { + _accepted0 = v; + }); + }, + ), + ), + ), + data: ThemeData( + primarySwatch: karColor, + unselectedWidgetColor: karColor, // Your color + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + dic['auction.read'], + style: TextStyle(color: cardColor), + ), + JumpToLink( + 'https://acala.network/', + text: ' ${dic['auction.term.0']}', + color: karColor, + ) + ], + ) + ], + ), + _signed + ? Container() + : Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Theme( + child: SizedBox( + height: 48, + width: 32, + child: Padding( + padding: EdgeInsets.only(right: 8), + child: Checkbox( + value: _accepted1, + onChanged: (v) { + setState(() { + _accepted1 = v; + }); + }, + ), + ), + ), + data: ThemeData( + primarySwatch: karColor, + unselectedWidgetColor: karColor, // Your color + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + dic['auction.meet'], + style: TextStyle(color: cardColor), + ), + JumpToLink( + 'https://acala.network/', + text: ' ${dic['auction.term.1']}', + color: karColor, + ) + ], + ) + ], + ), + _signed + ? Container() + : Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Theme( + child: SizedBox( + height: 48, + width: 32, + child: Padding( + padding: EdgeInsets.only(right: 8), + child: Checkbox( + value: _accepted2, + onChanged: (v) { + setState(() { + _accepted2 = v; + }); + }, + ), + ), + ), + data: ThemeData( + primarySwatch: karColor, + unselectedWidgetColor: karColor, // Your color + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + dic['auction.read'], + style: TextStyle(color: cardColor), + ), + JumpToLink( + 'https://acala.network/', + text: ' ${dic['auction.term.2']}', + color: karColor, + ) + ], + ) + ], + ), + Container( + margin: EdgeInsets.only(top: 16, bottom: 32), + child: _signed + ? RoundedButton( + text: dic['auction.contribute'], + color: karColor, + onPressed: () => Navigator.of(context).pushNamed( + KarCrowdLoanFormPage.route, + arguments: _account), + ) + : RoundedButton( + text: dic['auction.accept'], + icon: + _submitting ? CupertinoActivityIndicator() : null, + color: allAccepted && !_submitting + ? karColor + : Colors.grey, + onPressed: allAccepted ? _acceptAndSign : () => null, + ), + ) + ], + ) + ]); + } +} + +class CrowdLoanPageLayout extends StatelessWidget { + CrowdLoanPageLayout(this.title, this.children); + final String title; + final List children; + + @override + Widget build(BuildContext context) { + final cardColor = Theme.of(context).cardColor; + return Scaffold( + backgroundColor: Colors.black, + body: Stack( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Image.asset( + 'assets/images/public/kar_bg.png', + width: MediaQuery.of(context).size.width / 3, + ) + ], + ), + ListView( + padding: EdgeInsets.fromLTRB(16, 72, 16, 16), + children: children, + ), + Container( + height: 56, + margin: EdgeInsets.only(top: 24, left: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon(Icons.arrow_back_ios, color: cardColor), + onPressed: () => Navigator.of(context).pop()), + Text( + title, + style: TextStyle(color: cardColor, fontSize: 24), + ), + Container(width: 48) + ], + ), + ) + ], + ), + ); + } +} + +class JumpToLink extends StatefulWidget { + JumpToLink(this.url, {this.text, this.color}); + + final String text; + final String url; + final Color color; + + @override + _JumpToLinkState createState() => _JumpToLinkState(); +} + +class _JumpToLinkState extends State { + bool _loading = false; + + Future _launchUrl() async { + if (_loading) return; + + setState(() { + _loading = true; + }); + + await UI.launchURL(widget.url); + + setState(() { + _loading = false; + }); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(right: 4), + child: Text( + widget.text ?? widget.url, + style: TextStyle( + color: widget.color ?? Theme.of(context).primaryColor), + ), + ), + Icon(Icons.open_in_new, + size: 16, color: widget.color ?? Theme.of(context).primaryColor) + ], + ), + onTap: _launchUrl, + ); + } +} + +class CountdownPanel extends StatefulWidget { + CountdownPanel({ + this.cardColor, + this.cardTextColor, + this.textColor, + this.endTime, + }); + + final Color cardColor; + final Color cardTextColor; + final Color textColor; + final DateTime endTime; + + @override + _CountdownPanel createState() => _CountdownPanel(); +} + +class _CountdownPanel extends State { + Timer _timer; + + void _updateTime() { + setState(() { + _timer = Timer(Duration(seconds: 1), _updateTime); + }); + } + + Widget _buildCard(String text) { + return Container( + margin: EdgeInsets.only(left: 4, right: 2), + padding: EdgeInsets.only(left: 6, right: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(4)), + color: widget.cardColor, + ), + constraints: BoxConstraints(minWidth: 24), + child: Text( + text, + textAlign: TextAlign.center, + style: TextStyle( + color: widget.cardTextColor, + fontWeight: FontWeight.w700, + fontSize: 22, + fontFamily: 'BebasNeue'), + ), + ); + } + + String formatTime(int num) { + final str = num.toString(); + return str.length == 1 ? '0$str' : str; + } + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _updateTime(); + }); + } + + @override + void dispose() { + super.dispose(); + if (_timer != null) { + _timer.cancel(); + } + } + + @override + Widget build(BuildContext context) { + final now = DateTime.now(); + final left = widget.endTime.difference(now); + return Container( + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _buildCard(left.inDays.toString()), + Text( + 'days', + style: + TextStyle(fontWeight: FontWeight.bold, color: widget.textColor), + ), + _buildCard( + '${formatTime(left.inHours % 24)}:${formatTime(left.inMinutes % 60)}:${formatTime(left.inSeconds % 60)}'), + Text( + 'left', + style: + TextStyle(fontWeight: FontWeight.bold, color: widget.textColor), + ), + ], + ), + ); + } +} diff --git a/lib/pages/public/karPreAuctionPage.dart b/lib/pages/public/karPreAuctionPage.dart deleted file mode 100644 index 6c2aad0f..00000000 --- a/lib/pages/public/karPreAuctionPage.dart +++ /dev/null @@ -1,307 +0,0 @@ -import 'package:app/service/index.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:polkawallet_sdk/storage/types/keyPairData.dart'; -import 'package:polkawallet_sdk/webviewWithExtension/types/signExtrinsicParam.dart'; -import 'package:polkawallet_ui/components/addressIcon.dart'; -import 'package:polkawallet_ui/components/roundedButton.dart'; -import 'package:polkawallet_ui/pages/accountListPage.dart'; -import 'package:polkawallet_ui/utils/format.dart'; - -class KarPreAuctionPage extends StatefulWidget { - KarPreAuctionPage(this.service); - final AppService service; - - static final String route = '/public/kar/pre'; - - @override - _KarPreAuctionPageState createState() => _KarPreAuctionPageState(); -} - -class _KarPreAuctionPageState extends State { - final _emailRegEx = RegExp( - r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"); - final FocusNode _emailFocusNode = FocusNode(); - - KeyPairData _account = KeyPairData(); - - bool _submitting = false; - bool _emailValid = false; - String _email = ''; - - Future _selectAccount() async { - final res = await Navigator.of(context).pushNamed(AccountListPage.route, - arguments: AccountListPageParams( - list: widget.service.keyring.keyPairs, title: 'Accounts')); - if (res != null) { - setState(() { - _account = res; - }); - } - } - - void _onEmailChange(String value) { - final v = value.trim(); - final valid = _emailRegEx.hasMatch(v); - setState(() { - _emailValid = valid; - _email = v; - }); - } - - Future _signAndSubmit() async { - if (_submitting || !_emailValid) return; - - setState(() { - _submitting = true; - }); - final password = - await widget.service.account.getPassword(context, _account); - - if (password != null) { - final params = SignAsExtensionParam(); - params.msgType = "pub(bytes.sign)"; - params.request = { - "address": _account.address, - "data": 'Acala & Karura Testnet Festival Season 5', - }; - - final signRes = await widget.service.plugin.sdk.api.keyring - .signAsExtension(password, params); - final submitted = await widget.service.account - .postKarPreAuction(_account.pubKey, _email, signRes.signature); - if (submitted != null && (submitted['result'] ?? false)) { - await showCupertinoDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - content: Text('Success'), - actions: [ - CupertinoButton( - child: Text('OK'), - onPressed: () => Navigator.of(context).pop(), - ), - ], - ); - }, - ); - } else { - await showCupertinoDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - title: Text('Failed'), - content: Text(submitted['reason']), - actions: [ - CupertinoButton( - child: Text('OK'), - onPressed: () => Navigator.of(context).pop(), - ), - ], - ); - }, - ); - } - - setState(() { - _submitting = false; - }); - Navigator.of(context).pop(); - } else { - setState(() { - _submitting = false; - }); - } - } - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - _account = widget.service.keyring.current; - }); - }); - } - - @override - void dispose() { - super.dispose(); - _emailFocusNode.dispose(); - } - - @override - Widget build(BuildContext context) { - final cardColor = Theme.of(context).cardColor; - final karColor = Colors.red; - final grayColor = Colors.white70; - return Scaffold( - backgroundColor: Colors.black, - body: Stack( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Image.asset( - 'assets/images/public/kar_bg.png', - width: MediaQuery.of(context).size.width / 3, - ) - ], - ), - ListView( - padding: EdgeInsets.all(16), - children: [ - Row( - children: [ - Container( - margin: EdgeInsets.only(left: 4, bottom: 24, top: 64), - child: Image.asset( - 'assets/images/public/kar_logo.png', - width: MediaQuery.of(context).size.width * 2 / 3, - ), - ) - ], - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Pre-support the', - style: TextStyle(fontSize: 24, color: cardColor)), - Container( - margin: EdgeInsets.only(top: 8, bottom: 8), - padding: EdgeInsets.fromLTRB(16, 8, 16, 8), - decoration: BoxDecoration( - border: Border.all(color: karColor), - borderRadius: BorderRadius.all(Radius.circular(64))), - child: Row( - children: [ - Expanded( - child: FittedBox( - child: Text('Karura Parachain Auction', - style: TextStyle( - color: karColor, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic)))) - ], - ), - ), - Text( - 'Submit your email to be notified when the event is live!', - style: TextStyle(fontSize: 22, color: cardColor)), - Container( - margin: EdgeInsets.only(top: 24, bottom: 32), - child: Text( - 'Karura is the decentralized financial hub of Kusama. The blockchain, optimized for DeFi and powered by KAR, was built to enable scalable financial applications with micro gas fees and communication with other networks on Kusama, Polkadot, and beyond.', - style: TextStyle(color: cardColor), - ), - ) - ], - ), - Container( - margin: EdgeInsets.only(bottom: 4), - child: Divider(color: grayColor), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Address', - style: TextStyle(color: grayColor, fontSize: 18), - ), - GestureDetector( - child: Container( - margin: EdgeInsets.only(top: 8, bottom: 8), - padding: EdgeInsets.fromLTRB(12, 8, 12, 8), - decoration: BoxDecoration( - border: Border.all(color: grayColor), - borderRadius: BorderRadius.all(Radius.circular(64))), - child: Row( - children: [ - AddressIcon( - _account.address ?? '', - svg: _account.icon, - size: 36, - tapToCopy: false, - ), - Expanded( - child: Container( - margin: EdgeInsets.only(left: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _account.name ?? '', - style: - TextStyle(fontSize: 18, color: cardColor), - ), - Text( - Fmt.address(_account.address ?? ''), - style: - TextStyle(color: grayColor, fontSize: 14), - ), - ], - ), - )), - Icon( - Icons.keyboard_arrow_down, - size: 30, - color: grayColor, - ) - ], - ), - ), - onTap: _selectAccount, - ), - Text( - 'Email', - style: TextStyle(color: grayColor, fontSize: 18), - ), - Container( - margin: EdgeInsets.only(top: 8, bottom: 24), - child: CupertinoTextField( - padding: EdgeInsets.all(16), - placeholder: 'Email', - placeholderStyle: - TextStyle(color: grayColor, fontSize: 18), - style: TextStyle(color: cardColor, fontSize: 18), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(64)), - border: Border.all( - color: _emailFocusNode.hasFocus - ? karColor - : grayColor), - ), - cursorColor: karColor, - focusNode: _emailFocusNode, - onChanged: _onEmailChange, - ), - ), - Container( - margin: EdgeInsets.only(bottom: 32), - child: RoundedButton( - text: 'Sign & Submit', - icon: _submitting ? CupertinoActivityIndicator() : null, - color: - _emailValid && !_submitting ? karColor : Colors.grey, - onPressed: _signAndSubmit, - ), - ) - ], - ) - ], - ), - Container( - margin: EdgeInsets.only(top: 24, left: 8), - child: Row( - children: [ - IconButton( - icon: Icon(Icons.arrow_back_ios, color: cardColor), - onPressed: () => Navigator.of(context).pop()) - ], - ), - ) - ], - ), - ); - } -} diff --git a/lib/service/apiAccount.dart b/lib/service/apiAccount.dart index e16653e0..52d02c70 100644 --- a/lib/service/apiAccount.dart +++ b/lib/service/apiAccount.dart @@ -164,25 +164,20 @@ class ApiAccount { } Future checkBannerStatus(String pubKey) async { - final adClosed = apiRoot.store.storage.read(show_banner_status_key); + final adClosed = + apiRoot.store.storage.read('${show_banner_status_key}_$pubKey'); // check if banner was closed by user. if (adClosed != null) { apiRoot.store.account.setBannerVisible(false); - // check if account was submitted. - final status = await WalletApi.getKarPreAuctionInfo(pubKey); - if (status != null && !(status['status'] ?? false)) { - // show banner again if account was not submitted. - apiRoot.store.account.setBannerVisible(true); - } } else { apiRoot.store.account.setBannerVisible(true); } } - Future postKarPreAuction( - String pubKey, String email, String signature) async { - final submitted = - await WalletApi.postKarPreAuctionInfo(pubKey, email, signature); + Future postKarCrowdLoan(String address, BigInt amount, String email, + String referral, String signature) async { + final submitted = await WalletApi.postKarCrowdLoan( + address, amount, email, referral, signature); if (submitted != null && (submitted['result'] ?? false)) { apiRoot.store.account.setBannerVisible(false); } diff --git a/lib/service/index.dart b/lib/service/index.dart index db7dc4fe..cdda8bb4 100644 --- a/lib/service/index.dart +++ b/lib/service/index.dart @@ -1,3 +1,4 @@ +import 'package:app/common/consts.dart'; import 'package:app/service/apiAccount.dart'; import 'package:app/service/apiAssets.dart'; import 'package:app/store/index.dart'; @@ -7,11 +8,12 @@ import 'package:polkawallet_sdk/storage/keyring.dart'; import 'package:polkawallet_sdk/plugin/index.dart'; class AppService { - AppService(this.plugin, this.keyring, this.store); + AppService(this.plugin, this.keyring, this.store, this.buildTarget); final PolkawalletPlugin plugin; final Keyring keyring; final AppStore store; + final BuildTargets buildTarget; final subScan = SubScanApi(); diff --git a/lib/service/walletApi.dart b/lib/service/walletApi.dart index 0a781228..e65f50f5 100644 --- a/lib/service/walletApi.dart +++ b/lib/service/walletApi.dart @@ -5,6 +5,7 @@ import 'package:http/http.dart'; class WalletApi { static const String _endpoint = 'https://api.polkawallet.io'; + static const String _karEndpoint = 'https://crowdloan-api.laminar.codes'; static const String _jsCodeStorageKey = 'js_service_'; static const String _jsCodeStorageVersionKey = 'js_service_version_'; @@ -99,9 +100,9 @@ class WalletApi { } } - static Future getKarPreAuctionInfo(String pubKey) async { + static Future getKarCrowdLoanStarted() async { try { - Response res = await get('$_endpoint/crowdloan/info?address=$pubKey'); + final res = await get('$_endpoint/crowdloan/health'); if (res == null) { return null; } else { @@ -113,17 +114,72 @@ class WalletApi { } } - static Future postKarPreAuctionInfo( - String pubKey, String email, String signature) async { + static Future getKarCrowdLoanStatement() async { + try { + final res = await get('$_karEndpoint/statement'); + if (res == null) { + return null; + } else { + return jsonDecode(utf8.decode(res.bodyBytes)); + } + } catch (err) { + print(err); + return null; + } + } + + static Future verifyKarReferralCode(String code) async { + try { + final res = await get('$_karEndpoint/referral/$code'); + if (res == null) { + return null; + } else { + return jsonDecode(utf8.decode(res.bodyBytes)); + } + } catch (err) { + print(err); + return null; + } + } + + static Future postKarCrowdLoan(String address, BigInt amount, + String email, String referral, String signature) async { final headers = {"Content-type": "application/json", "Accept": "*/*"}; - final body = jsonEncode({ - "address": pubKey, + final body = { + "address": address, + "amount": amount.toString(), "email": email, "signature": signature, - }); + }; + if (referral.isNotEmpty) { + body.addAll({"referral": referral}); + } + try { + final res = await post('$_karEndpoint/sign', + headers: headers, body: jsonEncode(body)); + if (res == null) { + return null; + } else { + return jsonDecode(utf8.decode(res.bodyBytes)); + } + } catch (err) { + print(err); + return null; + } + } + + static Future postKarSubscribe(String email) async { + final headers = {"Content-type": "application/json", "Accept": "*/*"}; + final body = { + "fields": [ + {'name': 'email', 'value': email} + ], + }; try { - Response res = - await post('$_endpoint/crowdloan/sign', headers: headers, body: body); + final res = await post( + 'https://api.hsforms.com/submissions/v3/integration/submit/7522932/fc605148-482f-4302-a8d2-cece3251f7fc', + headers: headers, + body: jsonEncode(body)); if (res == null) { return null; } else { diff --git a/lib/utils/UI.dart b/lib/utils/UI.dart index ec879863..04628dc4 100644 --- a/lib/utils/UI.dart +++ b/lib/utils/UI.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:app/common/consts.dart'; +import 'package:app/main.dart'; import 'package:app/service/walletApi.dart'; import 'package:app/utils/i18n/index.dart'; import 'package:flutter/cupertino.dart'; @@ -46,7 +47,8 @@ class AppUI { ); } - static Future checkUpdate(BuildContext context, Map versions, + static Future checkUpdate( + BuildContext context, Map versions, BuildTargets buildTarget, {bool autoCheck = false}) async { if (versions == null || !Platform.isAndroid && !Platform.isIOS) return; String platform = Platform.isAndroid ? 'android' : 'ios'; @@ -55,20 +57,18 @@ class AppUI { final int latestCode = versions[platform]['version-code']; final String latestBeta = versions[platform]['version-beta']; final int latestCodeBeta = versions[platform]['version-code-beta']; + final int latestCodeStore = versions[platform]['version-code-store']; bool needUpdate = false; - if (autoCheck) { - if (latestCode > app_beta_version_code) { - // new version found - needUpdate = true; + if ((autoCheck ? latestCode : latestCodeBeta) > app_beta_version_code) { + // new version found + if (Platform.isAndroid && buildTarget == BuildTargets.playStore) { + needUpdate = (latestCodeStore) > app_beta_version_code; } else { - return; - } - } else { - if (latestCodeBeta > app_beta_version_code) { - // new version found needUpdate = true; } + } else { + if (autoCheck) return; } showCupertinoDialog( @@ -116,6 +116,12 @@ class AppUI { // go to ios download page UI.launchURL('https://polkawallet.io/#download'); } else if (Platform.isAndroid) { + if (buildTarget == BuildTargets.playStore) { + // go to google play page + UI.launchURL( + 'https://play.google.com/store/apps/details?id=io.polkawallet.www.polka_wallet'); + return; + } // download apk // START LISTENING FOR DOWNLOAD PROGRESS REPORTING EVENTS try { diff --git a/lib/utils/i18n/en/account.dart b/lib/utils/i18n/en/account.dart index 3c8718c9..c4e7eb4e 100644 --- a/lib/utils/i18n/en/account.dart +++ b/lib/utils/i18n/en/account.dart @@ -74,6 +74,6 @@ const Map enAccount = { 'guide.1': 'Offline signature', 'guide.2': 'DOT/KSM Staking & Governance', 'guide.3': 'Kusama social recovery', - 'guide.4': 'Acala Defi Platform', + 'guide.4': 'Acala Defi Hub', 'guide.enter': 'Enter Polkawallet', }; diff --git a/lib/utils/i18n/en/public.dart b/lib/utils/i18n/en/public.dart new file mode 100644 index 00000000..fc10b5a9 --- /dev/null +++ b/lib/utils/i18n/en/public.dart @@ -0,0 +1,30 @@ +const Map enPublic = { + 'auction.switch': 'Switching to Kusama...', + 'auction.support': 'Support the', + 'auction.kar': 'Parachain Auction', + 'auction.live': 'Event now live', + 'auction.finish': 'Event has ended', + 'auction.address': 'Address', + 'auction.read': 'I have read and accept the', + 'auction.meet': 'I have check and was meet the', + 'auction.term.0': 'Terms & Conditions', + 'auction.term.1': 'Qualified Person Criteria', + 'auction.term.2': 'Privacy Policy', + 'auction.accept': 'Accept & Sign', + 'auction.contribute': 'Contribute', + 'auction.email': 'Email', + 'auction.amount': 'Amount to Contribute (KSM)', + 'auction.balance': 'Available', + 'auction.referral': 'Referral Code (optional)', + 'auction.estimate': 'Estimated KAR Paradrop', + 'auction.note': + 'If Karura wins the auction, KAR rewards will be distributed upon genesis. Otherwise, the KSM locked will automatically bid for the next available auction. Check the Terms & Conditions for details.', + 'auction.init': 'Initial transferable', + 'auction.vest': 'Vested over the lease period', + 'auction.lease': 'Lease period', + 'auction.notify': + 'I agree to receive email communications about Karura and Acala including exclusive launch updates and liquidity provider program.', + 'auction.submit': 'Submit', + 'auction.invalid': 'Invalid', + 'auction.connecting': 'Connecting WSS', +}; diff --git a/lib/utils/i18n/index.dart b/lib/utils/i18n/index.dart index 15c446ef..23d7ec3f 100644 --- a/lib/utils/i18n/index.dart +++ b/lib/utils/i18n/index.dart @@ -1,19 +1,23 @@ import 'package:app/utils/i18n/en/account.dart'; import 'package:app/utils/i18n/en/assets.dart'; import 'package:app/utils/i18n/en/profile.dart'; +import 'package:app/utils/i18n/en/public.dart'; import 'package:app/utils/i18n/zh/account.dart'; import 'package:app/utils/i18n/zh/assets.dart'; import 'package:app/utils/i18n/zh/profile.dart'; +import 'package:app/utils/i18n/zh/public.dart'; const Map>> i18n_full_dic_app = { 'en': { 'account': enAccount, 'assets': enAssets, 'profile': enProfile, + 'public': enPublic, }, 'zh': { 'account': zhAccount, 'assets': zhAssets, 'profile': zhProfile, + 'public': zhPublic, } }; diff --git a/lib/utils/i18n/zh/account.dart b/lib/utils/i18n/zh/account.dart index e1b0c738..7feafa77 100644 --- a/lib/utils/i18n/zh/account.dart +++ b/lib/utils/i18n/zh/account.dart @@ -66,6 +66,6 @@ const Map zhAccount = { 'guide.1': '支持离线签名', 'guide.2': '参与 DOT/KSM 质押和治理', 'guide.3': '支持 Kusama 社交恢复', - 'guide.4': '支持 Acala Defi 平台', + 'guide.4': '支持 Acala Defi 中心', 'guide.enter': '进入 Polkawallet', }; diff --git a/lib/utils/i18n/zh/public.dart b/lib/utils/i18n/zh/public.dart new file mode 100644 index 00000000..5ce3da9f --- /dev/null +++ b/lib/utils/i18n/zh/public.dart @@ -0,0 +1,29 @@ +const Map zhPublic = { + 'auction.switch': '正在切换至 Kusama...', + 'auction.support': '支持', + 'auction.kar': '平行链拍卖', + 'auction.live': '活动进行中', + 'auction.finish': '活动已结束', + 'auction.address': '地址', + 'auction.read': '我已阅读并接受', + 'auction.meet': '我已检查并符合', + 'auction.term.0': '政策条款', + 'auction.term.1': '参与者标准', + 'auction.term.2': '隐私条款', + 'auction.accept': '接受并签名', + 'auction.contribute': '支持竞拍', + 'auction.email': '邮箱', + 'auction.amount': '贡献数量(KSM)', + 'auction.balance': '可用余额', + 'auction.referral': '邀请码(选填)', + 'auction.estimate': '预估奖励(KAR)', + 'auction.note': + '如果 Karura 赢得了卡槽拍卖,KAR 奖励将会在创世区块发放。否则,质押的 KSM 将用于下一次卡槽拍卖。详见《政策条款》。', + 'auction.init': '初始释放', + 'auction.vest': '租赁期线性释放', + 'auction.lease': '租赁期', + 'auction.notify': '我同意接收来自 Karura 和 Acala 相关活动动态的通知邮件。', + 'auction.submit': '提交', + 'auction.invalid': '无效的', + 'auction.connecting': '正在连接 WSS', +}; diff --git a/pubspec.lock b/pubspec.lock index 0d1c4598..1679d64a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -679,11 +679,9 @@ packages: polkawallet_plugin_chainx: dependency: "direct main" description: - path: "." - ref: "1659c6c5b92ef7deb38e3071284634cf1b6b40b7" - resolved-ref: "1659c6c5b92ef7deb38e3071284634cf1b6b40b7" - url: "https://github.com/true-eye/polkawallet_plugin_chainx.git" - source: git + path: "../../coding/polkawallet/polkawallet_plugin_chainx" + relative: true + source: path version: "0.0.1" polkawallet_plugin_kusama: dependency: "direct main" @@ -702,20 +700,18 @@ packages: source: git version: "0.1.5" polkawallet_sdk: - dependency: transitive + dependency: "direct overridden" description: - name: polkawallet_sdk - url: "https://pub.dartlang.org" - source: hosted + path: "../../coding/polkawallet/sdk" + relative: true + source: path version: "0.1.4" polkawallet_ui: - dependency: "direct main" + dependency: "direct overridden" description: - path: "." - ref: e81dc98d117f589f57d23d23affdf29022e81a4c - resolved-ref: e81dc98d117f589f57d23d23affdf29022e81a4c - url: "https://github.com/polkawallet-io/ui.git" - source: git + path: "../../coding/polkawallet/ui" + relative: true + source: path version: "0.1.4" pool: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index d3d03fe5..6ef5bc6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # 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: 2.0.2+2022 +version: 2.0.2+2023 environment: sdk: ">=2.10.0 <3.0.0" @@ -32,16 +32,11 @@ dependencies: biometric_storage: 1.0.1+5 update_app: ^0.1.5 device_info: ^0.4.2 - polkawallet_ui: - git: - url: https://github.com/polkawallet-io/ui.git - ref: e81dc98d117f589f57d23d23affdf29022e81a4c -# path: ../../coding/polkawallet/ui polkawallet_plugin_kusama: - git: - url: https://github.com/polkawallet-io/polkawallet_plugin_kusama.git - ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 -# path: ../../coding/polkawallet/polkawallet_plugin_kusama +# git: +# url: https://github.com/polkawallet-io/polkawallet_plugin_kusama.git +# ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 + path: ../../coding/polkawallet/polkawallet_plugin_kusama polkawallet_plugin_acala: git: url: https://github.com/AcalaNetwork/polkawallet_plugin_acala.git @@ -53,10 +48,10 @@ dependencies: ref: b115aa0ab5b341bcfa3b7f5e239ed22f2026f82b # path: ../../coding/polkawallet/polkawallet_plugin_laminar polkawallet_plugin_chainx: - git: - url: https://github.com/true-eye/polkawallet_plugin_chainx.git - ref: 1659c6c5b92ef7deb38e3071284634cf1b6b40b7 -# path: ../../coding/polkawallet/polkawallet_plugin_chainx +# git: +# url: https://github.com/true-eye/polkawallet_plugin_chainx.git +# ref: 1659c6c5b92ef7deb38e3071284634cf1b6b40b7 + path: ../../coding/polkawallet/polkawallet_plugin_chainx # polkawallet_plugin_edgeware: ## git: ## url: https://github.com/remzrn/polkawallet_plugin_edgeware.git @@ -69,10 +64,13 @@ dependencies: dependency_overrides: polkawallet_ui: - git: - url: https://github.com/polkawallet-io/ui.git - ref: e81dc98d117f589f57d23d23affdf29022e81a4c -# path: ../../coding/polkawallet/ui +# git: +# url: https://github.com/polkawallet-io/ui.git +# ref: e81dc98d117f589f57d23d23affdf29022e81a4c + path: ../../coding/polkawallet/ui + polkawallet_sdk: + path: ../../coding/polkawallet/sdk + dev_dependencies: flutter_test: sdk: flutter From 49303283c5a7c77e4a8aaafe3ed07f7b2a365709 Mon Sep 17 00:00:00 2001 From: shawn Date: Thu, 15 Apr 2021 16:43:41 +0800 Subject: [PATCH 2/5] remove observation account creation on import account page --- .../account/import/importAccountForm.dart | 224 +++--------------- lib/pages/public/karCrowdLoanFormPage.dart | 18 +- lib/pages/public/karCrowdLoanPage.dart | 6 + 3 files changed, 53 insertions(+), 195 deletions(-) diff --git a/lib/pages/account/import/importAccountForm.dart b/lib/pages/account/import/importAccountForm.dart index 7ec6301c..4bcdc01d 100644 --- a/lib/pages/account/import/importAccountForm.dart +++ b/lib/pages/account/import/importAccountForm.dart @@ -10,8 +10,6 @@ import 'package:flutter/material.dart'; import 'package:polkawallet_sdk/api/apiKeyring.dart'; import 'package:polkawallet_sdk/utils/i18n.dart'; import 'package:polkawallet_ui/components/roundedButton.dart'; -import 'package:polkawallet_ui/pages/scanPage.dart'; -import 'package:polkawallet_ui/utils/format.dart'; import 'package:polkawallet_ui/utils/i18n.dart'; class ImportAccountForm extends StatefulWidget { @@ -30,14 +28,12 @@ class _ImportAccountFormState extends State { 'mnemonic', 'rawSeed', 'keystore', - 'observe', ]; bool _supportBiometric = false; bool _enableBiometric = true; // if the biometric usage checkbox checked int _keySelection = 0; - bool _observationSubmitting = false; final _formKey = GlobalKey(); @@ -45,12 +41,6 @@ class _ImportAccountFormState extends State { final TextEditingController _nameCtrl = new TextEditingController(); final TextEditingController _passCtrl = new TextEditingController(); - final TextEditingController _observationAddressCtrl = - new TextEditingController(); - final TextEditingController _observationNameCtrl = - new TextEditingController(); - final TextEditingController _memoCtrl = new TextEditingController(); - String _keyCtrlText = ''; AccountAdvanceOptionParams _advanceOptions = AccountAdvanceOptionParams(); @@ -153,132 +143,6 @@ class _ImportAccountFormState extends State { ); } - Widget _buildAddressAndNameInput() { - final dic = I18n.of(context).getDic(i18n_full_dic_app, 'profile'); - return Column( - children: [ - Padding( - padding: EdgeInsets.only(left: 16, right: 16), - child: TextFormField( - decoration: InputDecoration( - hintText: dic['contact.address'], - labelText: dic['contact.address'], - suffix: GestureDetector( - child: Icon(Icons.camera_alt), - onTap: () async { - final acc = (await Navigator.of(context) - .pushNamed(ScanPage.route)) as QRCodeResult; - if (acc != null) { - setState(() { - _observationAddressCtrl.text = acc.address.address; - _observationNameCtrl.text = acc.address.name; - }); - } - }, - ), - ), - controller: _observationAddressCtrl, - validator: (v) { - if (!Fmt.isAddress(v.trim())) { - return dic['contact.address.error']; - } - return null; - }, - ), - ), - Padding( - padding: EdgeInsets.only(left: 16, right: 16), - child: TextFormField( - decoration: InputDecoration( - hintText: dic['contact.name'], - labelText: dic['contact.name'], - ), - controller: _observationNameCtrl, - validator: (v) { - return v.trim().length > 0 ? null : dic['contact.name.error']; - }, - ), - ), - Padding( - padding: EdgeInsets.only(left: 16, right: 16), - child: TextFormField( - decoration: InputDecoration( - hintText: dic['contact.memo'], - labelText: dic['contact.memo'], - ), - controller: _memoCtrl, - ), - ), - ], - ); - } - - Future _onAddObservationAccount() async { - setState(() { - _observationSubmitting = true; - }); - showCupertinoDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - title: Container(), - content: Container(height: 64, child: CupertinoActivityIndicator()), - ); - }, - ); - - final dic = I18n.of(context).getDic(i18n_full_dic_app, 'profile'); - final address = _observationAddressCtrl.text.trim(); - final pubKeyAddress = - await widget.service.plugin.sdk.api.account.decodeAddress([address]); - final pubKey = pubKeyAddress.keys.toList()[0]; - Map acc = { - 'address': address, - 'name': _observationNameCtrl.text, - 'memo': _memoCtrl.text, - 'observation': true, - 'pubKey': pubKey, - }; - // create new contact - int exist = widget.service.keyring.externals - .indexWhere((i) => i.address == address); - if (exist > -1) { - setState(() { - _observationSubmitting = false; - }); - Navigator.of(context).pop(); - - showCupertinoDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - title: Container(), - content: Text(dic['contact.exist']), - actions: [ - CupertinoButton( - child: Text( - I18n.of(context).getDic(i18n_full_dic_ui, 'common')['ok']), - onPressed: () => Navigator.of(context).pop(), - ), - ], - ); - }, - ); - } else { - final res = await widget.service.plugin.sdk.api.keyring - .addContact(widget.service.keyring, acc); - widget.service.plugin.changeAccount(res); - widget.service.store.assets - .loadCache(res, widget.service.plugin.basic.name); - - setState(() { - _observationSubmitting = false; - }); - // go to home page - Navigator.popUntil(context, ModalRoute.withName('/')); - } - } - String _validateInput(String v) { bool passed = false; final dic = I18n.of(context).getDic(i18n_full_dic_app, 'account'); @@ -334,9 +198,6 @@ class _ImportAccountFormState extends State { _nameCtrl.dispose(); _passCtrl.dispose(); _keyCtrl.dispose(); - _observationAddressCtrl.dispose(); - _observationNameCtrl.dispose(); - _memoCtrl.dispose(); super.dispose(); } @@ -400,17 +261,15 @@ class _ImportAccountFormState extends State { : Container(), _keySelection == 2 ? _buildNameAndPassInput() - : _keySelection == 3 - ? _buildAddressAndNameInput() - : AccountAdvanceOption( - api: widget.service.plugin.sdk.api.keyring, - seed: _keyCtrlText, - onChange: (AccountAdvanceOptionParams data) { - setState(() { - _advanceOptions = data; - }); - }, - ), + : AccountAdvanceOption( + api: widget.service.plugin.sdk.api.keyring, + seed: _keyCtrlText, + onChange: (AccountAdvanceOptionParams data) { + setState(() { + _advanceOptions = data; + }); + }, + ), ], ), ), @@ -419,43 +278,36 @@ class _ImportAccountFormState extends State { padding: EdgeInsets.all(16), child: RoundedButton( text: I18n.of(context).getDic(i18n_full_dic_ui, 'common')['next'], - onPressed: _observationSubmitting - ? null - : () async { - if (_formKey.currentState.validate() && - !(_advanceOptions.error ?? false)) { - if (_keySelection == 3) { - _onAddObservationAccount(); - return; - } - if (_keySelection == 2) { - widget.service.store.account.setNewAccount( - _nameCtrl.text.trim(), _passCtrl.text.trim()); - } - widget.service.store.account - .setNewAccountKey(_keyCtrl.text.trim()); - final saved = await widget.onSubmit({ - 'keyType': _keyOptions[_keySelection], - 'cryptoType': - _advanceOptions.type ?? CryptoType.sr25519, - 'derivePath': _advanceOptions.path ?? '', - 'finish': _keySelection == 2 ? true : null, - }); - if (saved) { - if (_supportBiometric && _enableBiometric) { - await _authBiometric(); - } + onPressed: () async { + if (_formKey.currentState.validate() && + !(_advanceOptions.error ?? false)) { + if (_keySelection == 2) { + widget.service.store.account.setNewAccount( + _nameCtrl.text.trim(), _passCtrl.text.trim()); + } + widget.service.store.account + .setNewAccountKey(_keyCtrl.text.trim()); + final saved = await widget.onSubmit({ + 'keyType': _keyOptions[_keySelection], + 'cryptoType': _advanceOptions.type ?? CryptoType.sr25519, + 'derivePath': _advanceOptions.path ?? '', + 'finish': _keySelection == 2 ? true : null, + }); + if (saved) { + if (_supportBiometric && _enableBiometric) { + await _authBiometric(); + } - widget.service.plugin - .changeAccount(widget.service.keyring.current); - widget.service.store.assets.loadCache( - widget.service.keyring.current, - widget.service.plugin.basic.name); - widget.service.store.account.resetNewAccount(); - Navigator.popUntil(context, ModalRoute.withName('/')); - } - } - }, + widget.service.plugin + .changeAccount(widget.service.keyring.current); + widget.service.store.assets.loadCache( + widget.service.keyring.current, + widget.service.plugin.basic.name); + widget.service.store.account.resetNewAccount(); + Navigator.popUntil(context, ModalRoute.withName('/')); + } + } + }, ), ), ], diff --git a/lib/pages/public/karCrowdLoanFormPage.dart b/lib/pages/public/karCrowdLoanFormPage.dart index f38adb43..525239ab 100644 --- a/lib/pages/public/karCrowdLoanFormPage.dart +++ b/lib/pages/public/karCrowdLoanFormPage.dart @@ -111,8 +111,8 @@ class _KarCrowdLoanFormPageState extends State { final res = await WalletApi.verifyKarReferralCode(v); print(res); // todo: valid2 = true for testing - final valid2 = true; - // final valid2 = res != null && res['result']; + // final valid2 = true; + final valid2 = res != null && res['result']; setState(() { _referral = v; _referralValid = valid2; @@ -134,15 +134,15 @@ class _KarCrowdLoanFormPageState extends State { .read('$kar_statement_store_key${account.pubKey}'); final amountInt = Fmt.tokenInt(_amount.toString(), decimals); // todo: add this post request while API is ready. - // final res = await WalletApi.postKarCrowdLoan( - // account.address, amountInt, _email, _referral, signed); - // print(res); - final res = {'result': true}; - if (res != null && res['result']) { + final signingRes = await WalletApi.postKarCrowdLoan( + account.address, amountInt, _email, _referral, signed); + print(signingRes); + // final signingRes = {'result': true}; + if (signingRes != null && signingRes['result']) { final dic = I18n.of(context).getDic(i18n_full_dic_app, 'public'); // todo: use response data while API is ready. - // final signingPayload = {'Sr25519': res['signingPayload']}; - final signingPayload = {'Sr25519': signed}; + final signingPayload = {'Sr25519': signingRes['signingPayload']}; + // final signingPayload = {'Sr25519': signed}; final res = (await Navigator.of(context).pushNamed(TxConfirmPage.route, arguments: TxConfirmParams( module: 'crowdloan', diff --git a/lib/pages/public/karCrowdLoanPage.dart b/lib/pages/public/karCrowdLoanPage.dart index ba0f11e8..0a08f9cd 100644 --- a/lib/pages/public/karCrowdLoanPage.dart +++ b/lib/pages/public/karCrowdLoanPage.dart @@ -84,6 +84,12 @@ class _KarCrowdLoanPageState extends State { final acc = res as KeyPairData; if (acc.pubKey == _account.pubKey) return; + // change account in app so we can get the balance + widget.service.keyring.setCurrent(acc); + widget.service.plugin.changeAccount(acc); + widget.service.store.assets + .loadCache(acc, widget.service.plugin.basic.name); + final signed = widget.service.store.storage .read('$kar_statement_store_key${acc.pubKey}'); From b0b30b84590f7bd7d0386b5324056c1fbb40927d Mon Sep 17 00:00:00 2001 From: shawn Date: Fri, 16 Apr 2021 17:13:01 +0800 Subject: [PATCH 3/5] kar crowdloan WIP & add estimate fee in transfer page --- ios/Runner.xcodeproj/project.pbxproj | 12 ++-- lib/app.dart | 5 +- lib/common/consts.dart | 4 +- lib/pages/assets/transfer/transferPage.dart | 33 ++++++++++- lib/pages/public/AdBanner.dart | 10 ++-- lib/pages/public/karCrowdLoanFormPage.dart | 7 +-- lib/pages/public/karCrowdLoanPage.dart | 64 ++++++++++++++++++++- lib/service/walletApi.dart | 15 +++++ lib/utils/i18n/en/public.dart | 1 + lib/utils/i18n/zh/public.dart | 1 + pubspec.lock | 24 ++++---- pubspec.yaml | 26 ++++----- 12 files changed, 154 insertions(+), 48 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index bdad65eb..31dfdb89 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = WQ5H736A22; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -370,7 +370,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.0.3; PRODUCT_BUNDLE_IDENTIFIER = io.polkawallet.polkawallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -493,7 +493,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = WQ5H736A22; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -509,7 +509,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.0.3; PRODUCT_BUNDLE_IDENTIFIER = io.polkawallet.polkawallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -526,7 +526,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = WQ5H736A22; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -542,7 +542,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.0.3; PRODUCT_BUNDLE_IDENTIFIER = io.polkawallet.polkawallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/app.dart b/lib/app.dart index 54c293c3..f3eb241d 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -216,9 +216,10 @@ class _WalletAppState extends State { } Future _changeToKusamaForKar() async { + final name = 'kusama'; await _changeNetwork( - widget.plugins.firstWhere((e) => e.basic.name == 'kusama')); - _service.store.assets.loadCache(_keyring.current, 'kusama'); + widget.plugins.firstWhere((e) => e.basic.name == name)); + _service.store.assets.loadCache(_keyring.current, name); } Future _changeNode(NetworkParams node) async { diff --git a/lib/common/consts.dart b/lib/common/consts.dart index bb48d4ca..5d6a7566 100644 --- a/lib/common/consts.dart +++ b/lib/common/consts.dart @@ -15,8 +15,8 @@ const prefixList = [ /// app versions enum BuildTargets { apk, playStore, dev } -const String app_beta_version = 'v2.0.2-beta.3'; -const int app_beta_version_code = 2023; +const String app_beta_version = 'v2.0.3-beta.1'; +const int app_beta_version_code = 2031; const show_guide_status_key = 'show_guide_status'; const show_banner_status_key = 'show_banner_status'; diff --git a/lib/pages/assets/transfer/transferPage.dart b/lib/pages/assets/transfer/transferPage.dart index 2bf5b697..1aa96bf7 100644 --- a/lib/pages/assets/transfer/transferPage.dart +++ b/lib/pages/assets/transfer/transferPage.dart @@ -11,6 +11,7 @@ import 'package:polkawallet_ui/components/currencyWithIcon.dart'; import 'package:polkawallet_ui/components/tapTooltip.dart'; import 'package:polkawallet_ui/components/txButton.dart'; import 'package:polkawallet_ui/pages/scanPage.dart'; +import 'package:polkawallet_sdk/api/types/txInfoData.dart'; import 'package:polkawallet_sdk/storage/types/keyPairData.dart'; import 'package:polkawallet_sdk/utils/i18n.dart'; import 'package:polkawallet_ui/utils/format.dart'; @@ -43,6 +44,8 @@ class _TransferPageState extends State { KeyPairData _accountTo; bool _keepAlive = true; + TxFeeEstimateResult _fee; + Future _onScan() async { final to = await Navigator.of(context).pushNamed(ScanPage.route); if (to == null) return; @@ -87,6 +90,22 @@ class _TransferPageState extends State { return null; } + Future _getTxFee({bool reload = false}) async { + if (_fee?.partialFee != null && !reload) { + return _fee.partialFee.toString(); + } + + final sender = TxSenderData(widget.service.keyring.current.address, + widget.service.keyring.current.pubKey); + final txInfo = TxInfoData('balances', 'transfer', sender); + final fee = await widget.service.plugin.sdk.api.tx.estimateFees( + txInfo, [widget.service.keyring.current.address, '10000000000']); + setState(() { + _fee = fee; + }); + return fee.partialFee.toString(); + } + Future _initAccountTo(String address) async { final acc = KeyPairData(); acc.address = address; @@ -110,6 +129,8 @@ class _TransferPageState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { + _getTxFee(); + final TransferPageParams args = ModalRoute.of(context).settings.arguments; if (args.address != null) { _initAccountTo(args.address); @@ -200,7 +221,17 @@ class _TransferPageState extends State { if (v.isEmpty) { return dic['amount.error']; } - if (Fmt.tokenInt(v, decimals) >= available) { + final feeLeft = available - + Fmt.tokenInt(v, decimals) - + (_keepAlive + ? Fmt.balanceInt(amountExist) + : BigInt.zero); + BigInt fee = BigInt.zero; + if (feeLeft < Fmt.tokenInt('0.02', decimals) && + _fee?.partialFee != null) { + fee = Fmt.balanceInt(_fee.partialFee.toString()); + } + if (feeLeft - fee < BigInt.zero) { return dic['amount.low']; } return null; diff --git a/lib/pages/public/AdBanner.dart b/lib/pages/public/AdBanner.dart index 7ce63f84..dddc603d 100644 --- a/lib/pages/public/AdBanner.dart +++ b/lib/pages/public/AdBanner.dart @@ -61,16 +61,16 @@ class _AdBannerState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - _getCrowdLoanStarted(); + // todo: only available in dev now + if (widget.service.buildTarget == BuildTargets.dev) { + _getCrowdLoanStarted(); + } }); } @override Widget build(BuildContext context) { - // todo: only available in dev now - final show = widget.connectedNode != null && - _started && - widget.service.buildTarget == BuildTargets.dev; + final show = widget.connectedNode != null && _started; final fullWidth = MediaQuery.of(context).size.width; final cardColor = Theme.of(context).cardColor; diff --git a/lib/pages/public/karCrowdLoanFormPage.dart b/lib/pages/public/karCrowdLoanFormPage.dart index 525239ab..28656d4b 100644 --- a/lib/pages/public/karCrowdLoanFormPage.dart +++ b/lib/pages/public/karCrowdLoanFormPage.dart @@ -157,9 +157,8 @@ class _KarCrowdLoanFormPageState extends State { ))) as Map; if (res != null) { if (_emailAccept) { - // todo: remove this await in production - final resTest = await WalletApi.postKarSubscribe(_email); - print(resTest); + // todo: update subscribe id in production + WalletApi.postKarSubscribe(_email); } await showCupertinoDialog( context: context, @@ -175,7 +174,7 @@ class _KarCrowdLoanFormPageState extends State { ); }, ); - Navigator.of(context).pop(); + Navigator.of(context).pop(res); } setState(() { diff --git a/lib/pages/public/karCrowdLoanPage.dart b/lib/pages/public/karCrowdLoanPage.dart index 0a08f9cd..1acac523 100644 --- a/lib/pages/public/karCrowdLoanPage.dart +++ b/lib/pages/public/karCrowdLoanPage.dart @@ -43,6 +43,8 @@ class _KarCrowdLoanPageState extends State { String _statement; bool _signed = false; + List _contributions = []; + Future _updateBestNumber() async { final res = await widget.service.plugin.sdk.webView .evalJavascript('api.derive.chain.bestNumber()'); @@ -54,6 +56,8 @@ class _KarCrowdLoanPageState extends State { } Future _getCrowdLoanInfo() async { + _getCrowdLoanHistory(); + if (widget.connectedNode == null) return; _updateBestNumber(); @@ -66,6 +70,17 @@ class _KarCrowdLoanPageState extends State { } } + Future _getCrowdLoanHistory() async { + final res = await WalletApi.getKarCrowdLoanHistory( + widget.service.keyring.current.address); + print(res); + if (mounted) { + setState(() { + _contributions = res; + }); + } + } + Future _getKarStatement() async { final res = await WalletApi.getKarCrowdLoanStatement(); print(res); @@ -130,8 +145,11 @@ class _KarCrowdLoanPageState extends State { _signed = true; }); - Navigator.of(context) + final res = await Navigator.of(context) .pushNamed(KarCrowdLoanFormPage.route, arguments: _account); + if (res != null) { + _getCrowdLoanInfo(); + } } else { setState(() { _submitting = false; @@ -171,6 +189,8 @@ class _KarCrowdLoanPageState extends State { @override Widget build(BuildContext context) { final dic = I18n.of(context).getDic(i18n_full_dic_app, 'public'); + final decimals = + (widget.service.plugin.networkState.tokenDecimals ?? [12])[0]; DateTime endTime = DateTime.now(); bool finished = false; @@ -374,7 +394,47 @@ class _KarCrowdLoanPageState extends State { ], ), _signed - ? Container() + ? _contributions.length == 0 + ? Container() + : Column( + children: [ + Text( + dic['auction.txs'], + style: TextStyle(color: cardColor), + ), + Column( + children: _contributions.map((e) { + return Row( + children: [ + Column( + children: [ + Row( + children: [ + Text( + 'Tx: ${Fmt.address(e['inBlock']['eventId'])}', + style: TextStyle( + color: Colors.white70), + ), + JumpToLink( + 'https://acala.network/', + text: + ' ${dic['auction.term.0']}', + color: karColor, + ) + ], + ), + Text(Fmt.dateTime( + DateTime.parse(e['createdAt']))) + ], + ), + Text( + '${Fmt.balance(e['amount'], decimals)} KSM') + ], + ); + }).toList(), + ) + ], + ) : Row( mainAxisAlignment: MainAxisAlignment.start, children: [ diff --git a/lib/service/walletApi.dart b/lib/service/walletApi.dart index e65f50f5..9c8a0093 100644 --- a/lib/service/walletApi.dart +++ b/lib/service/walletApi.dart @@ -128,6 +128,21 @@ class WalletApi { } } + static Future getKarCrowdLoanHistory(String address) async { + try { + final res = await get('$_karEndpoint/contributions/$address'); + if (res == null) { + return null; + } else { + print(jsonDecode(utf8.decode(res.bodyBytes))); + return jsonDecode(utf8.decode(res.bodyBytes)); + } + } catch (err) { + print(err); + return null; + } + } + static Future verifyKarReferralCode(String code) async { try { final res = await get('$_karEndpoint/referral/$code'); diff --git a/lib/utils/i18n/en/public.dart b/lib/utils/i18n/en/public.dart index fc10b5a9..dea4a86e 100644 --- a/lib/utils/i18n/en/public.dart +++ b/lib/utils/i18n/en/public.dart @@ -27,4 +27,5 @@ const Map enPublic = { 'auction.submit': 'Submit', 'auction.invalid': 'Invalid', 'auction.connecting': 'Connecting WSS', + 'auction.txs': 'My Contributions', }; diff --git a/lib/utils/i18n/zh/public.dart b/lib/utils/i18n/zh/public.dart index 5ce3da9f..07dec928 100644 --- a/lib/utils/i18n/zh/public.dart +++ b/lib/utils/i18n/zh/public.dart @@ -26,4 +26,5 @@ const Map zhPublic = { 'auction.submit': '提交', 'auction.invalid': '无效的', 'auction.connecting': '正在连接 WSS', + 'auction.txs': '我的贡献', }; diff --git a/pubspec.lock b/pubspec.lock index 1679d64a..4bccdee6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -670,11 +670,9 @@ packages: polkawallet_plugin_acala: dependency: "direct main" description: - path: "." - ref: "2c6e11ba04331b402c238e5bc6b8df72f2bf06d2" - resolved-ref: "2c6e11ba04331b402c238e5bc6b8df72f2bf06d2" - url: "https://github.com/AcalaNetwork/polkawallet_plugin_acala.git" - source: git + path: "../../coding/polkawallet/polkawallet_plugin_acala" + relative: true + source: path version: "0.1.3" polkawallet_plugin_chainx: dependency: "direct main" @@ -686,18 +684,18 @@ packages: polkawallet_plugin_kusama: dependency: "direct main" description: - path: "../../coding/polkawallet/polkawallet_plugin_kusama" - relative: true - source: path + path: "." + ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 + resolved-ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 + url: "https://github.com/polkawallet-io/polkawallet_plugin_kusama.git" + source: git version: "0.1.4" polkawallet_plugin_laminar: dependency: "direct main" description: - path: "." - ref: b115aa0ab5b341bcfa3b7f5e239ed22f2026f82b - resolved-ref: b115aa0ab5b341bcfa3b7f5e239ed22f2026f82b - url: "https://github.com/polkawallet-io/polkawallet_plugin_laminar.git" - source: git + path: "../../coding/polkawallet/polkawallet_plugin_laminar" + relative: true + source: path version: "0.1.5" polkawallet_sdk: dependency: "direct overridden" diff --git a/pubspec.yaml b/pubspec.yaml index 6ef5bc6f..02d077f5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # 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: 2.0.2+2023 +version: 2.0.3+2031 environment: sdk: ">=2.10.0 <3.0.0" @@ -33,20 +33,20 @@ dependencies: update_app: ^0.1.5 device_info: ^0.4.2 polkawallet_plugin_kusama: -# git: -# url: https://github.com/polkawallet-io/polkawallet_plugin_kusama.git -# ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 - path: ../../coding/polkawallet/polkawallet_plugin_kusama - polkawallet_plugin_acala: git: - url: https://github.com/AcalaNetwork/polkawallet_plugin_acala.git - ref: 2c6e11ba04331b402c238e5bc6b8df72f2bf06d2 -# path: ../../coding/polkawallet/polkawallet_plugin_acala + url: https://github.com/polkawallet-io/polkawallet_plugin_kusama.git + ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 +# path: ../../coding/polkawallet/polkawallet_plugin_kusama + polkawallet_plugin_acala: +# git: +# url: https://github.com/AcalaNetwork/polkawallet_plugin_acala.git +# ref: 2c6e11ba04331b402c238e5bc6b8df72f2bf06d2 + path: ../../coding/polkawallet/polkawallet_plugin_acala polkawallet_plugin_laminar: - git: - url: https://github.com/polkawallet-io/polkawallet_plugin_laminar.git - ref: b115aa0ab5b341bcfa3b7f5e239ed22f2026f82b -# path: ../../coding/polkawallet/polkawallet_plugin_laminar +# git: +# url: https://github.com/polkawallet-io/polkawallet_plugin_laminar.git +# ref: b115aa0ab5b341bcfa3b7f5e239ed22f2026f82b + path: ../../coding/polkawallet/polkawallet_plugin_laminar polkawallet_plugin_chainx: # git: # url: https://github.com/true-eye/polkawallet_plugin_chainx.git From 2f902c3b357b912a0842924b53b48cc20c81e12e Mon Sep 17 00:00:00 2001 From: shawn Date: Sat, 17 Apr 2021 18:10:24 +0800 Subject: [PATCH 4/5] add transfer limit with estimated fee & max amount button --- CHANGELOG.md | 6 +++ lib/main-dev.dart | 4 +- lib/main-google.dart | 4 +- lib/main.dart | 4 +- lib/pages/assets/transfer/transferPage.dart | 23 +++++++++ lib/utils/UI.dart | 1 - lib/utils/format.dart | 2 +- lib/utils/i18n/en/assets.dart | 1 + lib/utils/i18n/zh/assets.dart | 1 + pubspec.lock | 55 ++++++++++++++------- pubspec.yaml | 47 +++++++++--------- 11 files changed, 98 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be2f9484..dc3b88c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,4 +28,10 @@ # 2.0.2-beta - Add ChainX network support. - Hide tips by default in tx confirm page. +- Fix known Bugs. + +# 2.0.3-beta +- Add Edgeware network support. +- Update ChainX plugin. +- Update Laminar plugin to TC3. - Fix known Bugs. \ No newline at end of file diff --git a/lib/main-dev.dart b/lib/main-dev.dart index 0a8dab48..026dfaaa 100644 --- a/lib/main-dev.dart +++ b/lib/main-dev.dart @@ -5,7 +5,7 @@ import 'package:polkawallet_plugin_kusama/polkawallet_plugin_kusama.dart'; import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; import 'package:polkawallet_plugin_laminar/polkawallet_plugin_laminar.dart'; import 'package:polkawallet_plugin_chainx/polkawallet_plugin_chainx.dart'; -// import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; +import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; import 'package:get_storage/get_storage.dart'; @@ -18,7 +18,7 @@ void main() async { PluginAcala(), PluginLaminar(), PluginChainX(), - // PluginEdgeware(), + PluginEdgeware(), ]; runApp(WalletApp(_plugins, BuildTargets.dev)); diff --git a/lib/main-google.dart b/lib/main-google.dart index cbc24d1a..1decd8bf 100644 --- a/lib/main-google.dart +++ b/lib/main-google.dart @@ -5,7 +5,7 @@ import 'package:polkawallet_plugin_kusama/polkawallet_plugin_kusama.dart'; import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; import 'package:polkawallet_plugin_laminar/polkawallet_plugin_laminar.dart'; import 'package:polkawallet_plugin_chainx/polkawallet_plugin_chainx.dart'; -// import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; +import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; import 'package:get_storage/get_storage.dart'; @@ -18,7 +18,7 @@ void main() async { PluginAcala(), PluginLaminar(), PluginChainX(), - // PluginEdgeware(), + PluginEdgeware(), ]; runApp(WalletApp(_plugins, BuildTargets.playStore)); diff --git a/lib/main.dart b/lib/main.dart index dd77039f..336c6b71 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,7 @@ import 'package:polkawallet_plugin_kusama/polkawallet_plugin_kusama.dart'; import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; import 'package:polkawallet_plugin_laminar/polkawallet_plugin_laminar.dart'; import 'package:polkawallet_plugin_chainx/polkawallet_plugin_chainx.dart'; -// import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; +import 'package:polkawallet_plugin_edgeware/polkawallet_plugin_edgeware.dart'; import 'package:get_storage/get_storage.dart'; @@ -18,7 +18,7 @@ void main() async { PluginAcala(), PluginLaminar(), PluginChainX(), - // PluginEdgeware(), + PluginEdgeware(), ]; runApp(WalletApp(_plugins, BuildTargets.apk)); diff --git a/lib/pages/assets/transfer/transferPage.dart b/lib/pages/assets/transfer/transferPage.dart index 1aa96bf7..07230239 100644 --- a/lib/pages/assets/transfer/transferPage.dart +++ b/lib/pages/assets/transfer/transferPage.dart @@ -106,6 +106,23 @@ class _TransferPageState extends State { return fee.partialFee.toString(); } + Future _setMaxAmount(BigInt available, String amountExist) async { + final decimals = + (widget.service.plugin.networkState.tokenDecimals ?? [12])[0]; + final fee = await _getTxFee(); + // keep double amount of estimated fee + final max = available - + Fmt.balanceInt(fee) * BigInt.two - + (_keepAlive ? Fmt.balanceInt(amountExist) : BigInt.zero); + if (mounted) { + setState(() { + _amountCtrl.text = max > BigInt.zero + ? Fmt.bigIntToDouble(max, decimals).toStringAsFixed(8) + : '0'; + }); + } + } + Future _initAccountTo(String address) async { final acc = KeyPairData(); acc.address = address; @@ -212,6 +229,12 @@ class _TransferPageState extends State { decimals, lengthMax: 6, )})', + suffix: GestureDetector( + child: Text(dic['amount.max'], + style: TextStyle( + color: Theme.of(context).primaryColor)), + onTap: () => _setMaxAmount(available, amountExist), + ), ), inputFormatters: [UI.decimalInputFormatter(decimals)], controller: _amountCtrl, diff --git a/lib/utils/UI.dart b/lib/utils/UI.dart index 04628dc4..276c8724 100644 --- a/lib/utils/UI.dart +++ b/lib/utils/UI.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:io'; import 'package:app/common/consts.dart'; -import 'package:app/main.dart'; import 'package:app/service/walletApi.dart'; import 'package:app/utils/i18n/index.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/utils/format.dart b/lib/utils/format.dart index 0f49dff1..786b728e 100644 --- a/lib/utils/format.dart +++ b/lib/utils/format.dart @@ -1,6 +1,6 @@ class AppFmt { static bool checkPassword(String pass) { - var reg = RegExp(r'^(?![0-9]+$)(?![a-zA-Z]+$)[\S]{6,18}$'); + var reg = RegExp(r'^(?![0-9]+$)(?![a-zA-Z]+$)[\S]{6,32}$'); return reg.hasMatch(pass); } } diff --git a/lib/utils/i18n/en/assets.dart b/lib/utils/i18n/en/assets.dart index 3c3131e9..e54a0f03 100644 --- a/lib/utils/i18n/en/assets.dart +++ b/lib/utils/i18n/en/assets.dart @@ -12,6 +12,7 @@ const Map enAssets = { 'address.error': 'Invalid Address', 'address.subscan': 'More on Subscan', 'amount': 'Amount', + 'amount.max': 'Max', 'amount.error': 'Invalid amount', 'amount.low': 'Insufficient balance', 'amount.exist': 'existential deposit', diff --git a/lib/utils/i18n/zh/assets.dart b/lib/utils/i18n/zh/assets.dart index 18bfdce6..195684c4 100644 --- a/lib/utils/i18n/zh/assets.dart +++ b/lib/utils/i18n/zh/assets.dart @@ -11,6 +11,7 @@ const Map zhAssets = { 'address.error': '无效地址', 'address.subscan': '前往 Subscan 查看更多', 'amount': '数量', + 'amount.max': '最大值', 'amount.error': '格式错误', 'amount.low': '余额不足', 'amount.exist': '存活余额', diff --git a/pubspec.lock b/pubspec.lock index 4bccdee6..63978428 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -670,47 +670,64 @@ packages: polkawallet_plugin_acala: dependency: "direct main" description: - path: "../../coding/polkawallet/polkawallet_plugin_acala" - relative: true - source: path + path: "." + ref: "8714c27a4f46e3f0277ab58b42eea547f7d33095" + resolved-ref: "8714c27a4f46e3f0277ab58b42eea547f7d33095" + url: "https://github.com/AcalaNetwork/polkawallet_plugin_acala.git" + source: git version: "0.1.3" polkawallet_plugin_chainx: dependency: "direct main" description: - path: "../../coding/polkawallet/polkawallet_plugin_chainx" - relative: true - source: path + path: "." + ref: "07b601d0aaae0b173e7a18006f7502b9bb55b32d" + resolved-ref: "07b601d0aaae0b173e7a18006f7502b9bb55b32d" + url: "https://github.com/true-eye/polkawallet_plugin_chainx.git" + source: git + version: "0.0.1" + polkawallet_plugin_edgeware: + dependency: "direct main" + description: + path: "." + ref: acdfd8bddce2075542e4678ef55855805700c752 + resolved-ref: acdfd8bddce2075542e4678ef55855805700c752 + url: "https://github.com/remzrn/polkawallet_plugin_edgeware.git" + source: git version: "0.0.1" polkawallet_plugin_kusama: dependency: "direct main" description: path: "." - ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 - resolved-ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 + ref: a4ae23cc229016babf82fb0e6c85a6cb46ec16a3 + resolved-ref: a4ae23cc229016babf82fb0e6c85a6cb46ec16a3 url: "https://github.com/polkawallet-io/polkawallet_plugin_kusama.git" source: git version: "0.1.4" polkawallet_plugin_laminar: dependency: "direct main" description: - path: "../../coding/polkawallet/polkawallet_plugin_laminar" - relative: true - source: path + path: "." + ref: a6bcda75198f64b70e0f13f6c71f067b72d536b0 + resolved-ref: a6bcda75198f64b70e0f13f6c71f067b72d536b0 + url: "https://github.com/polkawallet-io/polkawallet_plugin_laminar.git" + source: git version: "0.1.5" polkawallet_sdk: dependency: "direct overridden" description: - path: "../../coding/polkawallet/sdk" - relative: true - source: path - version: "0.1.4" + name: polkawallet_sdk + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" polkawallet_ui: dependency: "direct overridden" description: - path: "../../coding/polkawallet/ui" - relative: true - source: path - version: "0.1.4" + path: "." + ref: "51ebe9ffe2c9a0c524aadffc03cbe413d891fa57" + resolved-ref: "51ebe9ffe2c9a0c524aadffc03cbe413d891fa57" + url: "https://github.com/polkawallet-io/ui.git" + source: git + version: "0.1.5" pool: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 02d077f5..4749960d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,27 +35,27 @@ dependencies: polkawallet_plugin_kusama: git: url: https://github.com/polkawallet-io/polkawallet_plugin_kusama.git - ref: f6f73b6896a6239f980063589fe1e1e3ef74ad78 + ref: a4ae23cc229016babf82fb0e6c85a6cb46ec16a3 # path: ../../coding/polkawallet/polkawallet_plugin_kusama polkawallet_plugin_acala: -# git: -# url: https://github.com/AcalaNetwork/polkawallet_plugin_acala.git -# ref: 2c6e11ba04331b402c238e5bc6b8df72f2bf06d2 - path: ../../coding/polkawallet/polkawallet_plugin_acala + git: + url: https://github.com/AcalaNetwork/polkawallet_plugin_acala.git + ref: 8714c27a4f46e3f0277ab58b42eea547f7d33095 +# path: ../../coding/polkawallet/polkawallet_plugin_acala polkawallet_plugin_laminar: -# git: -# url: https://github.com/polkawallet-io/polkawallet_plugin_laminar.git -# ref: b115aa0ab5b341bcfa3b7f5e239ed22f2026f82b - path: ../../coding/polkawallet/polkawallet_plugin_laminar + git: + url: https://github.com/polkawallet-io/polkawallet_plugin_laminar.git + ref: a6bcda75198f64b70e0f13f6c71f067b72d536b0 +# path: ../../coding/polkawallet/polkawallet_plugin_laminar polkawallet_plugin_chainx: -# git: -# url: https://github.com/true-eye/polkawallet_plugin_chainx.git -# ref: 1659c6c5b92ef7deb38e3071284634cf1b6b40b7 - path: ../../coding/polkawallet/polkawallet_plugin_chainx -# polkawallet_plugin_edgeware: -## git: -## url: https://github.com/remzrn/polkawallet_plugin_edgeware.git -## ref: 1e06fd124615d4dfee52ff5e6232361cacd0dcff + git: + url: https://github.com/true-eye/polkawallet_plugin_chainx.git + ref: 07b601d0aaae0b173e7a18006f7502b9bb55b32d +# path: ../../coding/polkawallet/polkawallet_plugin_chainx + polkawallet_plugin_edgeware: + git: + url: https://github.com/remzrn/polkawallet_plugin_edgeware.git + ref: acdfd8bddce2075542e4678ef55855805700c752 # path: ../../coding/polkawallet/polkawallet_plugin_edgeware # The following adds the Cupertino Icons font to your application. @@ -64,12 +64,13 @@ dependencies: dependency_overrides: polkawallet_ui: -# git: -# url: https://github.com/polkawallet-io/ui.git -# ref: e81dc98d117f589f57d23d23affdf29022e81a4c - path: ../../coding/polkawallet/ui - polkawallet_sdk: - path: ../../coding/polkawallet/sdk + git: + url: https://github.com/polkawallet-io/ui.git + ref: 51ebe9ffe2c9a0c524aadffc03cbe413d891fa57 +# path: ../../coding/polkawallet/ui + polkawallet_sdk: ^0.1.5 +# polkawallet_sdk: +# path: ../../coding/polkawallet/sdk dev_dependencies: flutter_test: From 8adde5a1e90605e62238c62bea4761e66b812119 Mon Sep 17 00:00:00 2001 From: shawn Date: Mon, 19 Apr 2021 10:32:47 +0800 Subject: [PATCH 5/5] update version code --- lib/common/consts.dart | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/common/consts.dart b/lib/common/consts.dart index 5d6a7566..344c53a8 100644 --- a/lib/common/consts.dart +++ b/lib/common/consts.dart @@ -15,8 +15,8 @@ const prefixList = [ /// app versions enum BuildTargets { apk, playStore, dev } -const String app_beta_version = 'v2.0.3-beta.1'; -const int app_beta_version_code = 2031; +const String app_beta_version = 'v2.0.3-beta.2'; +const int app_beta_version_code = 2032; const show_guide_status_key = 'show_guide_status'; const show_banner_status_key = 'show_banner_status'; diff --git a/pubspec.yaml b/pubspec.yaml index 4749960d..fcca6b9b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # 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: 2.0.3+2031 +version: 2.0.3+2032 environment: sdk: ">=2.10.0 <3.0.0"