From 20103f4105ffab5b5b8ec75e4430da6464a03b30 Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Fri, 14 Aug 2020 22:00:52 +0200 Subject: [PATCH] Last Updates for the UI --- Cargo.lock | 133 ++----------- lib/main.dart | 1 + lib/router/router.dart | 4 + lib/router/router.gr.dart | 10 + lib/screens/bounty_screen.dart | 184 ++++-------------- lib/screens/logs_screen.dart | 37 ++++ lib/screens/main_screen.dart | 9 +- lib/screens/screens.dart | 1 + lib/screens/splash_screen.dart | 10 + lib/services/bounty_service.dart | 7 +- lib/services/client/dev_client_service.dart | 48 +++-- lib/services/client/prod_client_service.dart | 5 +- .../client/sunshine_client_service.dart | 2 +- lib/services/logger_service.dart | 21 ++ lib/services/services.dart | 1 + lib/setup.iconfig.dart | 2 + lib/ui/submission_item.dart | 113 +++++++++++ native/sunshine/src/lib.rs | 2 +- packages/sunshine/lib/dto.dart | 34 +++- packages/sunshine/lib/sunshine_client.dart | 9 +- packages/sunshine/pubspec.lock | 2 +- pubspec.lock | 38 ++-- pubspec.yaml | 2 + 23 files changed, 369 insertions(+), 306 deletions(-) create mode 100644 lib/screens/logs_screen.dart create mode 100644 lib/services/logger_service.dart diff --git a/Cargo.lock b/Cargo.lock index 1593c1c..82b07ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -705,9 +705,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.2" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term 0.11.0", "atty", @@ -2070,9 +2070,9 @@ checksum = "2ee15951c035f79eddbef745611ec962f63f4558f1dadf98ab723cc603487c6f" [[package]] name = "ipfs-embed" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273d44a3affa0ead19c839b9a2ec3a1731bcc8d02104ef9b4e1c0e5e889a74d1" +checksum = "c9d01d1a2c641d8daceb2d497bde9fd1b193d6d74676e9c699738fd418245a02" dependencies = [ "async-std", "ip_network", @@ -6477,27 +6477,10 @@ dependencies = [ "sunshine-identity-ffi", ] -[[package]] -name = "sunshine-bank" -version = "0.2.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" -dependencies = [ - "clear_on_drop", - "frame-support", - "frame-system", - "parity-scale-codec", - "sp-runtime", - "sp-std", - "sunshine-bounty-utils", - "sunshine-donate", - "sunshine-org", - "sunshine-vote", -] - [[package]] name = "sunshine-bounty" version = "0.2.1" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" +source = "git+https://github.com/sunshine-protocol/sunshine-bounty#e6395c9b08c9a4fc2b5314f5dd33e2a7ca9631d8" dependencies = [ "clear_on_drop", "frame-support", @@ -6511,7 +6494,7 @@ dependencies = [ [[package]] name = "sunshine-bounty-client" version = "0.2.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" +source = "git+https://github.com/sunshine-protocol/sunshine-bounty#e6395c9b08c9a4fc2b5314f5dd33e2a7ca9631d8" dependencies = [ "async-std", "frame-support", @@ -6531,7 +6514,7 @@ dependencies = [ [[package]] name = "sunshine-bounty-ffi" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" +source = "git+https://github.com/sunshine-protocol/sunshine-bounty#e6395c9b08c9a4fc2b5314f5dd33e2a7ca9631d8" dependencies = [ "anyhow", "ipld-block-builder", @@ -6546,7 +6529,7 @@ dependencies = [ [[package]] name = "sunshine-bounty-utils" version = "0.2.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" +source = "git+https://github.com/sunshine-protocol/sunshine-bounty#e6395c9b08c9a4fc2b5314f5dd33e2a7ca9631d8" dependencies = [ "clear_on_drop", "derive-new", @@ -6561,7 +6544,7 @@ dependencies = [ [[package]] name = "sunshine-client" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine#13e7ca5dd5715ec9607689592e218bc56269e58f" +source = "git+https://github.com/sunshine-protocol/sunshine#ba3083640f42d51377ced6e04e76b214855f9658" dependencies = [ "async-trait", "ipld-block-builder", @@ -6578,7 +6561,7 @@ dependencies = [ [[package]] name = "sunshine-client-utils" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine-core#b15fa47a17ea53107598332f9fc8c99d0b9dab75" +source = "git+https://github.com/sunshine-protocol/sunshine-core#c0ebde51041ba66bb0a4ad14daf67d2f7e8f3834" dependencies = [ "anyhow", "async-std", @@ -6600,7 +6583,7 @@ dependencies = [ [[package]] name = "sunshine-codec" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine-core#b15fa47a17ea53107598332f9fc8c99d0b9dab75" +source = "git+https://github.com/sunshine-protocol/sunshine-core#c0ebde51041ba66bb0a4ad14daf67d2f7e8f3834" dependencies = [ "anyhow", "hash-db", @@ -6609,26 +6592,10 @@ dependencies = [ "thiserror", ] -[[package]] -name = "sunshine-court" -version = "0.2.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" -dependencies = [ - "clear_on_drop", - "frame-support", - "frame-system", - "parity-scale-codec", - "sp-runtime", - "sp-std", - "sunshine-bounty-utils", - "sunshine-org", - "sunshine-vote", -] - [[package]] name = "sunshine-crypto" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine-core#b15fa47a17ea53107598332f9fc8c99d0b9dab75" +source = "git+https://github.com/sunshine-protocol/sunshine-core#c0ebde51041ba66bb0a4ad14daf67d2f7e8f3834" dependencies = [ "aead 0.2.0", "anyhow", @@ -6655,21 +6622,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "sunshine-donate" -version = "0.2.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" -dependencies = [ - "clear_on_drop", - "frame-support", - "frame-system", - "parity-scale-codec", - "sp-runtime", - "sp-std", - "sunshine-bounty-utils", - "sunshine-org", -] - [[package]] name = "sunshine-faucet-client" version = "0.2.0" @@ -6710,7 +6662,7 @@ dependencies = [ [[package]] name = "sunshine-ffi-utils" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine-core#b15fa47a17ea53107598332f9fc8c99d0b9dab75" +source = "git+https://github.com/sunshine-protocol/sunshine-core#c0ebde51041ba66bb0a4ad14daf67d2f7e8f3834" dependencies = [ "allo-isolate", "async-std", @@ -6766,7 +6718,7 @@ dependencies = [ [[package]] name = "sunshine-keystore" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine-core#b15fa47a17ea53107598332f9fc8c99d0b9dab75" +source = "git+https://github.com/sunshine-protocol/sunshine-core#c0ebde51041ba66bb0a4ad14daf67d2f7e8f3834" dependencies = [ "anyhow", "async-std", @@ -6781,7 +6733,7 @@ dependencies = [ [[package]] name = "sunshine-node" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine#13e7ca5dd5715ec9607689592e218bc56269e58f" +source = "git+https://github.com/sunshine-protocol/sunshine#ba3083640f42d51377ced6e04e76b214855f9658" dependencies = [ "futures 0.3.5", "hex-literal", @@ -6809,26 +6761,10 @@ dependencies = [ "sunshine-runtime", ] -[[package]] -name = "sunshine-org" -version = "0.2.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" -dependencies = [ - "clear_on_drop", - "frame-support", - "frame-system", - "orml-utilities", - "parity-scale-codec", - "serde", - "sp-runtime", - "sp-std", - "sunshine-bounty-utils", -] - [[package]] name = "sunshine-pallet-utils" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine-core#b15fa47a17ea53107598332f9fc8c99d0b9dab75" +source = "git+https://github.com/sunshine-protocol/sunshine-core#c0ebde51041ba66bb0a4ad14daf67d2f7e8f3834" dependencies = [ "blake2-rfc", "hash-db", @@ -6842,7 +6778,7 @@ dependencies = [ [[package]] name = "sunshine-runtime" version = "0.1.0" -source = "git+https://github.com/sunshine-protocol/sunshine#13e7ca5dd5715ec9607689592e218bc56269e58f" +source = "git+https://github.com/sunshine-protocol/sunshine#ba3083640f42d51377ced6e04e76b214855f9658" dependencies = [ "frame-executive", "frame-support", @@ -6868,46 +6804,11 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder-runner", - "sunshine-bank", "sunshine-bounty", "sunshine-bounty-utils", - "sunshine-court", - "sunshine-donate", "sunshine-faucet-pallet", "sunshine-identity-pallet", - "sunshine-org", "sunshine-pallet-utils", - "sunshine-treasury", - "sunshine-vote", -] - -[[package]] -name = "sunshine-treasury" -version = "0.0.1" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" -dependencies = [ - "clear_on_drop", - "frame-support", - "frame-system", - "parity-scale-codec", - "serde", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "sunshine-vote" -version = "0.2.0" -source = "git+https://github.com/sunshine-protocol/sunshine-bounty#b470a6802069d2b6ba31bcac4bc52e02bd8763aa" -dependencies = [ - "clear_on_drop", - "frame-support", - "frame-system", - "parity-scale-codec", - "sp-runtime", - "sp-std", - "sunshine-bounty-utils", - "sunshine-org", ] [[package]] diff --git a/lib/main.dart b/lib/main.dart index 793f3e7..de25457 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -26,5 +26,6 @@ Future main() async { Future _setup() async { await configureDependencies(environment: prod); Intl.defaultLocale = 'en_US'; + // other pre-start setup goes here } diff --git a/lib/router/router.dart b/lib/router/router.dart index a981f07..2165ac9 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -31,6 +31,10 @@ export 'router.gr.dart'; AdaptiveRoute(page: BountyScreen), AdaptiveRoute(page: CreateBountyScreen), AdaptiveRoute(page: SubmitForBountyScreen), + AdaptiveRoute( + page: LoggerScreen, + maintainState: true, + ), ], generateNavigationHelperExtension: false, ) diff --git a/lib/router/router.gr.dart b/lib/router/router.gr.dart index 5b77782..1b88963 100644 --- a/lib/router/router.gr.dart +++ b/lib/router/router.gr.dart @@ -35,6 +35,7 @@ class Routes { static const String bountyScreen = '/bounty-screen'; static const String createBountyScreen = '/create-bounty-screen'; static const String submitForBountyScreen = '/submit-for-bounty-screen'; + static const String loggerScreen = '/logger-screen'; static const all = { blankScreen, splashScreen, @@ -53,6 +54,7 @@ class Routes { bountyScreen, createBountyScreen, submitForBountyScreen, + loggerScreen, }; } @@ -82,6 +84,7 @@ class Router extends RouterBase { RouteDef(Routes.bountyScreen, page: BountyScreen), RouteDef(Routes.createBountyScreen, page: CreateBountyScreen), RouteDef(Routes.submitForBountyScreen, page: SubmitForBountyScreen), + RouteDef(Routes.loggerScreen, page: LoggerScreen), ]; @override Map get pagesMap => _pagesMap; @@ -208,6 +211,13 @@ class Router extends RouterBase { settings: data, ); }, + LoggerScreen: (data) { + return buildAdaptivePageRoute( + builder: (context) => LoggerScreen(), + settings: data, + maintainState: true, + ); + }, }; } diff --git a/lib/screens/bounty_screen.dart b/lib/screens/bounty_screen.dart index 61db03c..01764e8 100644 --- a/lib/screens/bounty_screen.dart +++ b/lib/screens/bounty_screen.dart @@ -40,12 +40,14 @@ class _BountyScreenState extends State { icon: const Icon(Icons.launch), onPressed: () => launch(bounty.issue.htmlUrl), ), - IconButton( - icon: const Icon(Icons.monetization_on), - onPressed: () => { - // TODO(shekohex): show Contribute screen - }, - ) + // not important for now. + // IconButton( + // icon: const Icon(Icons.monetization_on), + // onPressed: () => { + // + // TODO(shekohex): show Contribute screen + // }, + // ) ], ), body: Stack( @@ -69,153 +71,53 @@ class _BountyScreenState extends State { initialData: const [], future: _bountyService.listBountySubmissions(bounty.info.id), builder: (context, snapshot) { - return Column( - children: [ - ...snapshot.data.map(buildSubmission), - ], - ); + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: SunshineLoading()); + } else { + return Column( + children: [ + ...snapshot.data?.map((e) => SubmissionItem(e)) ?? [], + ], + ); + } }, ), SizedBox(height: 74.h.toDouble()), ], ), ), - Padding( - padding: const EdgeInsets.only(bottom: 8), - child: Button( - text: 'Submit for the bounty', - variant: ButtonVariant.success, - onPressed: () async { - final res = await ExtendedNavigator.root.push( - Routes.submitForBountyScreen, - arguments: SubmitForBountyScreenArguments(bounty: bounty), + FutureBuilder( + initialData: false, + future: _bountyService.canSubmitFor(bounty), + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Button( + text: 'Submit for the bounty', + variant: ButtonVariant.success, + onPressed: () async { + final res = await ExtendedNavigator.root.push( + Routes.submitForBountyScreen, + arguments: + SubmitForBountyScreenArguments(bounty: bounty), + ); + if (res != null) { + // refresh + setState(() {}); + } + }, + ), ); - if (res != null) { - // refresh - setState(() {}); - } - }, - ), + } else { + return const SizedBox(); + } + }, ), ], ), ); } - - // TODO(shekohex): move this widget to a seprate file in ui - Widget buildSubmission(BountySubmission e) { - final numberFormat = NumberFormat.compactCurrency( - decimalDigits: 0, - symbol: '', - ); - return Dismissible( - key: Key(e.info.id.toString()), - background: Container(color: Colors.green), - confirmDismiss: (c) async { - await showCupertinoDialog( - context: context, - builder: (_) => AlertDialog( - title: Text( - 'Approve?', - style: TextStyle( - color: Colors.white, - fontSize: 16.ssp.toDouble(), - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.start, - overflow: TextOverflow.ellipsis, - ), - backgroundColor: AppColors.mainBackground, - content: Text( - 'Are you sure want to approve this submission?', - style: TextStyle( - color: Colors.white, - fontSize: 14.ssp.toDouble(), - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.start, - ), - actions: [ - FlatButton( - child: const Text('Approve'), - onPressed: () { - // TODO(shekohex): approve this submission - }, - ), - FlatButton( - child: const Text('Close'), - onPressed: () { - ExtendedNavigator.root.pop(); - }, - ), - ], - ), - ); - return false; - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Container( - height: 58, - color: AppColors.lightBackgroud, - child: Padding( - padding: EdgeInsets.only( - top: 12.h.toDouble(), - bottom: 12.h.toDouble(), - left: 12.w.toDouble(), - ), - child: Row( - children: [ - Container( - width: 28.w.toDouble(), - height: 28.h.toDouble(), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - border: const Border.fromBorderSide( - BorderSide( - color: Colors.white, - width: 0.2, - ), - ), - ), - child: Image( - image: NetworkImage( - e.issue.user.avatarUrl, - ), - fit: BoxFit.contain, - ), - ), - SizedBox(width: 10.w.toDouble()), - FittedBox( - fit: BoxFit.scaleDown, - child: Text( - '#${e.info.submitter} (@${e.issue.user.login}) ' - 'Submitted for ${numberFormat.format(e.info.amount)}', - style: TextStyle( - color: const Color(0xFF989898), - fontSize: 14.ssp.toDouble(), - ), - ), - ), - const Expanded(child: SizedBox()), - FittedBox( - fit: BoxFit.scaleDown, - child: IconButton( - icon: const Icon( - Icons.launch, - color: Colors.white, - ), - onPressed: () => launch(e.issue.htmlUrl), - ), - ), - SizedBox(width: 8.w.toDouble()), - ], - ), - ), - ), - ), - ); - } } class _BountyBody extends StatelessWidget { diff --git a/lib/screens/logs_screen.dart b/lib/screens/logs_screen.dart new file mode 100644 index 0000000..4f4bc80 --- /dev/null +++ b/lib/screens/logs_screen.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:sunshine/sunshine.dart'; +import 'package:xterm/frontend/terminal_view.dart'; +import 'package:xterm/xterm.dart'; + +class LoggerScreen extends StatefulWidget { + @override + _LoggerScreenState createState() => _LoggerScreenState(); +} + +class _LoggerScreenState extends State { + LoggerService _loggerService; + @override + void initState() { + super.initState(); + _loggerService = GetIt.I.get(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const MyAppBar(title: 'Logger'), + body: StreamBuilder( + stream: _loggerService.stream, + builder: (context, snapshot) { + final _terminal = Terminal( + onInput: (_) {}, + ); + _loggerService.items.forEach(_terminal.write); + return TerminalView( + terminal: _terminal, + ); + }, + ), + ); + } +} diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 76d33f0..637f6f8 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -108,13 +108,18 @@ class _MainScreenState extends State { bounty: bounty, ), onLongPress: () => launch(bounty.issue.htmlUrl), - onTap: () { - ExtendedNavigator.root.push( + onTap: () async { + debugPrint('Open Bounty ${bounty.info.id}'); + final res = await ExtendedNavigator.root.push( Routes.bountyScreen, arguments: BountyScreenArguments( bounty: bounty, ), ); + if (res != null) { + // refresh + setState(() {}); + } }, ); }, diff --git a/lib/screens/screens.dart b/lib/screens/screens.dart index 99c6b79..5a0816b 100644 --- a/lib/screens/screens.dart +++ b/lib/screens/screens.dart @@ -4,6 +4,7 @@ export 'bounty_screen.dart'; export 'create_bounty_screen.dart'; export 'generate_account_screen.dart'; export 'intro_screen.dart'; +export 'logs_screen.dart'; export 'main_screen.dart'; export 'recover_account_screen.dart'; export 'splash_screen.dart'; diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index 0ea859a..e54f54a 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:shake/shake.dart'; import 'package:sunshine/sunshine.dart'; class SplashScreen extends StatefulWidget { @@ -9,11 +10,20 @@ class SplashScreen extends StatefulWidget { class _SplashScreenState extends State { KeyService _keyService; ClientService _clientService; + @override void initState() { super.initState(); _keyService = GetIt.I.get(); _clientService = GetIt.I.get(); + GetIt.I.get().listenToLogs(); + // Shake the Device to view logs ;) + final shakeDetector = ShakeDetector.autoStart(onPhoneShake: () { + ExtendedNavigator.root.push(Routes.loggerScreen); + }); + // ignore: cascade_invocations + shakeDetector.startListening(); + debugPrint('Shake the device to see the logs'); } @override diff --git a/lib/services/bounty_service.dart b/lib/services/bounty_service.dart index 89aeb0a..40745f3 100644 --- a/lib/services/bounty_service.dart +++ b/lib/services/bounty_service.dart @@ -69,7 +69,7 @@ class BountyService { info.issueNumber.toInt(), ); if (issue == null) { - return null; + continue; } result.add(BountySubmission(info: info, issue: issue)); } @@ -113,4 +113,9 @@ class BountyService { Future contibuteToBounty(BigInt bountyId, BigInt amount) { return _clientService.contibuteToBounty(bountyId, amount); } + + Future canSubmitFor(Bounty bounty) async { + final uid = await _clientService.uid(); + return uid != bounty.info.depositer; + } } diff --git a/lib/services/client/dev_client_service.dart b/lib/services/client/dev_client_service.dart index 0e1e23d..babc233 100644 --- a/lib/services/client/dev_client_service.dart +++ b/lib/services/client/dev_client_service.dart @@ -6,7 +6,7 @@ import 'client_service.dart'; @dev @LazySingleton(as: ClientService) class DevClientService implements ClientService { - String _uid = '1'; + String _uid; String _balance = '1000000'; String _password = '12345678'; @@ -31,9 +31,7 @@ class DevClientService implements ClientService { _password = password; _uid = '1'; _balance = '0'; - await Future.delayed( - const Duration(seconds: 2), - ); + await Future.delayed(const Duration(seconds: 2)); return _uid; } @@ -48,6 +46,7 @@ class DevClientService implements ClientService { @override Future balance() async { + await Future.delayed(const Duration(milliseconds: 200)); return _balance ?? '100000000'; } @@ -58,28 +57,36 @@ class DevClientService implements ClientService { @override Future uid() async { + await Future.delayed(const Duration(milliseconds: 200)); return _uid; } @override Future mint() async { + await Future.delayed(const Duration(milliseconds: 200)); _balance = (int.parse(_balance) + 1000000).toString(); return BigInt.from(1000000); } @override Future approveBounty(BigInt submissionId) async { - return '1000'; + await Future.delayed(const Duration(milliseconds: 999)); + final sub = await getSubmission(submissionId); + final bounty = await getBounty(sub.bountyId); + bounty.approve(sub); + return sub.amount.toString(); } @override Future contibuteToBounty(BigInt bountyId, BigInt amount) async { + await Future.delayed(const Duration(milliseconds: 999)); final bounty = await getBounty(bountyId); return bounty?.contibute(amount).toString() ?? '0'; } @override Future getBounty(BigInt bountyId) async { + await Future.delayed(const Duration(milliseconds: 200)); return _mockedBounties.firstWhere( (e) => e.id == bountyId, orElse: () => null, @@ -88,6 +95,7 @@ class DevClientService implements ClientService { @override Future getSubmission(BigInt submissionId) async { + await Future.delayed(const Duration(milliseconds: 100)); return _mockedSubmissions.firstWhere( (e) => e.id == submissionId, orElse: () => null, @@ -99,15 +107,21 @@ class DevClientService implements ClientService { BigInt bountyId, ) async { await Future.delayed(const Duration(milliseconds: 1200)); - return _mockedSubmissions.where((e) => e.bountyId == bountyId).toList() - ..sort((a, b) => a.id.compareTo(b.id)); + return _mockedSubmissions + .where((e) => e.awaitingReview) + .where((e) => e.bountyId == bountyId) + .toList() + ..sort((a, b) => a.id.compareTo(b.id)); } @override Future> listOpenBounties(BigInt min) async { await Future.delayed(const Duration(milliseconds: 1200)); - return _mockedBounties.where((e) => e.total >= min).toList() - ..sort((a, b) => a.id.compareTo(b.id)); + return _mockedBounties + .where((e) => e.isOpen) + .where((e) => e.total >= min) + .toList() + ..sort((a, b) => a.id.compareTo(b.id)); } @override @@ -117,6 +131,7 @@ class DevClientService implements ClientService { BigInt issueNumber, BigInt amount, ) async { + await Future.delayed(const Duration(milliseconds: 1500)); final last = _mockedBounties.last; final v = BountyInformation( depositer: await uid(), @@ -138,15 +153,15 @@ class DevClientService implements ClientService { BigInt issueNumber, BigInt amount, ) async { + await Future.delayed(const Duration(milliseconds: 1500)); final last = _mockedSubmissions.last; final v = BountySubmissionInformation( - id: last.id, + id: last.id + BigInt.one, bountyId: bountyId, issueNumber: issueNumber, repoName: repoName, repoOwner: repoOwner, amount: amount, - approved: false, awaitingReview: true, submitter: await uid(), ); @@ -189,11 +204,11 @@ final List _mockedBounties = [ issueNumber: BigInt.from(30), ), ]; + final List _mockedSubmissions = [ BountySubmissionInformation( id: BigInt.from(1), bountyId: BigInt.from(1), - approved: false, awaitingReview: true, submitter: '1', amount: BigInt.from(6000), @@ -204,12 +219,11 @@ final List _mockedSubmissions = [ BountySubmissionInformation( id: BigInt.from(2), bountyId: BigInt.from(1), - approved: false, awaitingReview: true, submitter: '3', - amount: BigInt.from(6000), - repoOwner: 'ArweaveTeam', - repoName: 'Bounties', - issueNumber: BigInt.from(30), + amount: BigInt.from(6500), + repoOwner: 'aragonone', + repoName: 'hack-for-freedom', + issueNumber: BigInt.from(7), ), ]; diff --git a/lib/services/client/prod_client_service.dart b/lib/services/client/prod_client_service.dart index 2e7f2ff..c044373 100644 --- a/lib/services/client/prod_client_service.dart +++ b/lib/services/client/prod_client_service.dart @@ -103,13 +103,14 @@ class ProdClientService implements ClientService { String repoName, BigInt issueNumber, BigInt amount, - ) { - return _sunshineClientService.postBounty( + ) async { + final id = await _sunshineClientService.postBounty( repoOwner, repoName, issueNumber, amount, ); + return id; } @override diff --git a/lib/services/client/sunshine_client_service.dart b/lib/services/client/sunshine_client_service.dart index 31e10dd..6167de2 100644 --- a/lib/services/client/sunshine_client_service.dart +++ b/lib/services/client/sunshine_client_service.dart @@ -7,6 +7,6 @@ class SunshineClientService extends SunshineClient { SunshineClientService({PathProviderService pathProviderService}) : super( root: pathProviderService.applicationDocumentsDirectory, - url: 'ws://10.0.2.2:9944', + url: 'ws://51.11.244.93:9944', ); } diff --git a/lib/services/logger_service.dart b/lib/services/logger_service.dart new file mode 100644 index 0000000..f91e31a --- /dev/null +++ b/lib/services/logger_service.dart @@ -0,0 +1,21 @@ +import 'dart:async'; + +import 'package:injectable/injectable.dart'; +import 'package:frusty_logger/frusty_logger.dart'; + +@lazySingleton +class LoggerService { + List items = []; + StreamController _streamController; + void _onLogEvent(String event) { + items.add('$event\r\n'); + _streamController.add(null); + } + + void listenToLogs() { + FrustyLogger.addListener(_onLogEvent); + _streamController = StreamController.broadcast(); + } + + Stream get stream => _streamController.stream; +} diff --git a/lib/services/services.dart b/lib/services/services.dart index cd3a406..6d4a8e7 100644 --- a/lib/services/services.dart +++ b/lib/services/services.dart @@ -3,5 +3,6 @@ export 'bounty_service.dart'; export 'client/client_service.dart'; export 'github_service.dart'; export 'key_service.dart'; +export 'logger_service.dart'; export 'path_provider_service.dart'; export 'wallet_service.dart'; diff --git a/lib/setup.iconfig.dart b/lib/setup.iconfig.dart index 516505c..2d07957 100644 --- a/lib/setup.iconfig.dart +++ b/lib/setup.iconfig.dart @@ -8,6 +8,7 @@ import 'package:sunshine/services/client/dev_client_service.dart'; import 'package:sunshine/services/client/client_service.dart'; import 'package:sunshine/services/github_service.dart'; import 'package:sunshine/services/key_service.dart'; +import 'package:sunshine/services/logger_service.dart'; import 'package:sunshine/services/path_provider_service.dart'; import 'package:sunshine/core/register_module.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -23,6 +24,7 @@ Future $initGetIt(GetIt g, {String environment}) async { g.registerLazySingleton(() => GithubService()); g.registerLazySingleton( () => KeyService(clientService: g())); + g.registerLazySingleton(() => LoggerService()); final pathProviderService = await registerModule.pathProvider; g.registerLazySingleton(() => pathProviderService); final sharedPreferences = await registerModule.prefs; diff --git a/lib/ui/submission_item.dart b/lib/ui/submission_item.dart index e69de29..e0a9539 100644 --- a/lib/ui/submission_item.dart +++ b/lib/ui/submission_item.dart @@ -0,0 +1,113 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:sunshine/models/models.dart'; +import 'package:sunshine/sunshine.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class SubmissionItem extends StatelessWidget { + const SubmissionItem(this.bountySubmission); + final BountySubmission bountySubmission; + @override + Widget build(BuildContext context) { + final bountyService = GetIt.I.get(); + final e = bountySubmission; + final numberFormat = NumberFormat.compactCurrency( + decimalDigits: 0, + symbol: '', + ); + return Dismissible( + key: Key(e.info.id.toString()), + background: Container(color: Colors.green), + confirmDismiss: (dir) async { + if (dir == DismissDirection.startToEnd) { + final snackbar = SnackBar( + content: Text('Approving (@${e.issue.user.login}) ...'), + backgroundColor: Colors.green, + action: SnackBarAction( + label: 'Undo', + onPressed: () {}, + textColor: Colors.white, + ), + duration: const Duration(seconds: 5), + ); + final result = Scaffold.of(context).showSnackBar(snackbar); + final reason = await result.closed; + if (reason == SnackBarClosedReason.action) { + return false; + } else { + await bountyService.approveBounty(e.info.id); + // ignore: unawaited_futures + Future.delayed(const Duration(milliseconds: 200)) + .whenComplete(() => ExtendedNavigator.root.pop(true)); + return true; + } + } else { + return false; + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Container( + height: 58, + color: AppColors.lightBackgroud, + child: Padding( + padding: EdgeInsets.only( + top: 12.h.toDouble(), + bottom: 12.h.toDouble(), + left: 12.w.toDouble(), + ), + child: Row( + children: [ + Container( + width: 28.w.toDouble(), + height: 28.h.toDouble(), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + border: const Border.fromBorderSide( + BorderSide( + color: Colors.white, + width: 0.2, + ), + ), + ), + child: Image( + image: NetworkImage( + e.issue.user.avatarUrl, + ), + fit: BoxFit.contain, + ), + ), + SizedBox(width: 10.w.toDouble()), + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + '#${e.info.submitter} (@${e.issue.user.login}) ' + 'Submitted for ' + '${numberFormat.format(e.info.amount.toInt())}', + style: TextStyle( + color: const Color(0xFF989898), + fontSize: 14.ssp.toDouble(), + ), + ), + ), + const Expanded(child: SizedBox()), + FittedBox( + fit: BoxFit.scaleDown, + child: IconButton( + icon: const Icon( + Icons.launch, + color: Colors.white, + ), + onPressed: () => launch(e.issue.htmlUrl), + ), + ), + SizedBox(width: 8.w.toDouble()), + ], + ), + ), + ), + ), + ); + } +} diff --git a/native/sunshine/src/lib.rs b/native/sunshine/src/lib.rs index 18fc4a3..51e5652 100644 --- a/native/sunshine/src/lib.rs +++ b/native/sunshine/src/lib.rs @@ -6,7 +6,7 @@ frusty_logger::include_ffi!( with_config: Config::new( Level::Info, FilterBuilder::new() - .parse("sunshine,sc_information,substrate_subxt") + .parse("sunshine,sc_information,substrate_subxt,sunshine-bounty-ffi") .build() ) ); diff --git a/packages/sunshine/lib/dto.dart b/packages/sunshine/lib/dto.dart index c85d678..5d56506 100644 --- a/packages/sunshine/lib/dto.dart +++ b/packages/sunshine/lib/dto.dart @@ -10,7 +10,8 @@ class BountyInformation { this.issueNumber, this.depositer, BigInt total, - }) : _total = total; + }) : _total = total, + _isOpen = true; factory BountyInformation.fromString(String data) { final d = json.decode(data); @@ -32,9 +33,14 @@ class BountyInformation { final BigInt issueNumber; final String depositer; BigInt _total; + bool _isOpen = true; BigInt get total => _total; + bool get isOpen => _isOpen; static List buildList(String str) { + if (str == null || str.isEmpty) { + return []; + } final data = json.decode(str) as List; return data.map((e) => BountyInformation.fromJSON(e)).toList(); } @@ -43,6 +49,11 @@ class BountyInformation { return _total += amount; } + void approve(BountySubmissionInformation submission) { + submission.approve(); + _isOpen = false; + } + @override String toString() { return 'BountyInformation { id: $id, depositer: $depositer }'; @@ -58,9 +69,9 @@ class BountySubmissionInformation { this.bountyId, this.submitter, this.amount, - this.awaitingReview, - this.approved, - }); + bool awaitingReview, + }) : _awaitingReview = awaitingReview, + _approved = !awaitingReview; factory BountySubmissionInformation.fromString(String data) { final d = jsonDecode(data); @@ -77,7 +88,6 @@ class BountySubmissionInformation { submitter: data['submitter'] as String, amount: BigInt.parse(data['amount'].toString()), awaitingReview: data['awaiting_review'] as bool, - approved: data['approved'] as bool, ); } final BigInt id; @@ -87,14 +97,22 @@ class BountySubmissionInformation { final BigInt bountyId; final String submitter; final BigInt amount; - final bool awaitingReview; - final bool approved; + bool _awaitingReview = true; + bool _approved = false; + + bool get awaitingReview => _awaitingReview; + bool get approved => _approved; static List buildList(String json) { - if (json.isEmpty) { + if (json == null || json.isEmpty) { return []; } final data = jsonDecode(json) as List ?? []; return data.map((e) => BountySubmissionInformation.fromJSON(e)).toList(); } + + void approve() { + _awaitingReview = false; + _approved = true; + } } diff --git a/packages/sunshine/lib/sunshine_client.dart b/packages/sunshine/lib/sunshine_client.dart index b345f88..f3c5fe7 100644 --- a/packages/sunshine/lib/sunshine_client.dart +++ b/packages/sunshine/lib/sunshine_client.dart @@ -170,7 +170,7 @@ class SunshineClient { BigInt issueNumber, BigInt amount, ) { - final completer = Completer(); + final completer = Completer(); final port = singleCompletePort(completer); ffi.client_bounty_post( port.nativePort, @@ -179,7 +179,8 @@ class SunshineClient { issueNumber.toInt(), amount.toUtf8Pointer(), ); - return completer.future; + + return completer.future.then((v) => BigInt.from(v)); } Future submitForBounty( @@ -190,7 +191,7 @@ class SunshineClient { BigInt amount, ) { print('Submit for bounty id: $bountyId'); - final completer = Completer(); + final completer = Completer(); final port = singleCompletePort(completer); ffi.client_bounty_submit( port.nativePort, @@ -200,7 +201,7 @@ class SunshineClient { issueNumber.toInt(), amount.toUtf8Pointer(), ); - return completer.future; + return completer.future.then((v) => BigInt.from(v)); } Future approveBounty(BigInt submissionId) { diff --git a/packages/sunshine/pubspec.lock b/packages/sunshine/pubspec.lock index d2a0c59..a3f962c 100644 --- a/packages/sunshine/pubspec.lock +++ b/packages/sunshine/pubspec.lock @@ -72,7 +72,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "2cb4b76bd45d8fe58784709adf0092b6a8d8906d" + resolved-ref: a331223b8962eef953616f9d4ae3dd6fdcd4f089 url: "https://github.com/sunshine-protocol/frusty-logger.git" source: git version: "0.1.0" diff --git a/pubspec.lock b/pubspec.lock index c4ddb15..91da62b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -84,7 +84,7 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.3.10" + version: "1.3.11" build_runner: dependency: "direct dev" description: @@ -113,13 +113,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "7.1.0" - cbor: - dependency: transitive - description: - name: cbor - url: "https://pub.dartlang.org" - source: hosted - version: "3.2.0" characters: dependency: transitive description: @@ -280,7 +273,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "2cb4b76bd45d8fe58784709adf0092b6a8d8906d" + resolved-ref: a331223b8962eef953616f9d4ae3dd6fdcd4f089 url: "https://github.com/sunshine-protocol/frusty-logger.git" source: git version: "0.1.0" @@ -437,7 +430,7 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.6+3" + version: "0.9.7" node_interop: dependency: transitive description: @@ -578,6 +571,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + sensors: + dependency: transitive + description: + name: sensors + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2+2" + shake: + dependency: "direct main" + description: + name: shake + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" shared_preferences: dependency: "direct main" description: @@ -619,7 +626,7 @@ packages: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "0.7.7" + version: "0.7.9" shelf_web_socket: dependency: transitive description: @@ -757,7 +764,7 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.1.2+1" uuid: dependency: transitive description: @@ -807,6 +814,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.0" + xterm: + dependency: "direct main" + description: + name: xterm + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c42a4a8..d476532 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: http_extensions_cache: ^0.1.3 shimmer: ^1.1.1 flutter_markdown: ^0.4.3 + shake: ^0.1.0 + xterm: ^0.1.0 sunshine_ffi: path: ./packages/sunshine timeago: