From d36e8b6c9353507d3e9771393d761e4e433e2405 Mon Sep 17 00:00:00 2001 From: Mati Date: Tue, 12 Sep 2023 14:17:15 -0300 Subject: [PATCH 01/23] feat(payment method selector): factors out a component in order to make it reusable PE-3173 --- .../payment_method_selector_widget.dart | 149 +++++++++++++++++ lib/components/upload_form.dart | 154 ++++-------------- .../components/turbo_balance_widget.dart | 2 +- lib/turbo/topup/views/topup_modal.dart | 4 +- 4 files changed, 180 insertions(+), 129 deletions(-) create mode 100644 lib/components/payment_method_selector_widget.dart diff --git a/lib/components/payment_method_selector_widget.dart b/lib/components/payment_method_selector_widget.dart new file mode 100644 index 0000000000..49f577e5d5 --- /dev/null +++ b/lib/components/payment_method_selector_widget.dart @@ -0,0 +1,149 @@ +import 'package:ardrive/blocs/upload/upload_cubit.dart'; +import 'package:ardrive/core/upload/cost_calculator.dart'; +import 'package:ardrive/turbo/topup/views/topup_modal.dart'; +import 'package:ardrive_ui/ardrive_ui.dart'; +import 'package:arweave/utils.dart'; +import 'package:flutter/material.dart'; + +class PaymentMethodSelector extends StatelessWidget { + final UploadMethod uploadMethod; + final UploadCostEstimate? costEstimateTurbo; + final UploadCostEstimate costEstimateAr; + final bool hasNoTurboBalance; + final bool isTurboUploadPossible; + final String arBalance; + final String turboCredits; + final void Function() onTurboTopupSucess; + final void Function() onArSelect; + final void Function() onTurboSelect; + + const PaymentMethodSelector({ + super.key, + required this.uploadMethod, + required this.costEstimateTurbo, + required this.costEstimateAr, + required this.hasNoTurboBalance, + required this.isTurboUploadPossible, + required this.arBalance, + required this.turboCredits, + required this.onTurboTopupSucess, + required this.onArSelect, + required this.onTurboSelect, + }); + + @override + Widget build(context) { + return _buildContent(context); + } + + Widget _buildContent(BuildContext context) { + return Column( + children: [ + Text( + 'Payment method:', // TODO: localize + style: ArDriveTypography.body.buttonLargeBold(), + ), + const SizedBox( + height: 8, + ), + ArDriveRadioButtonGroup( + size: 15, + onChanged: (index, value) { + switch (index) { + case 0: + if (value) { + onArSelect(); + } + break; + + case 1: + if (value) { + onTurboSelect(); + } + break; + } + }, + options: [ + // FIXME: rename to RadioButtonOption + RadioButtonOptions( + value: uploadMethod == UploadMethod.ar, + // TODO: Localization + text: 'Cost: ${winstonToAr(costEstimateAr.totalCost)} AR', + textStyle: ArDriveTypography.body.buttonLargeBold(), + ), + if (costEstimateTurbo != null && isTurboUploadPossible) + RadioButtonOptions( + value: uploadMethod == UploadMethod.turbo, + // TODO: Localization + text: hasNoTurboBalance + ? '' + : 'Cost: ${winstonToAr(costEstimateTurbo!.totalCost)} Credits', + textStyle: ArDriveTypography.body.buttonLargeBold(), + content: hasNoTurboBalance + ? GestureDetector( + onTap: () { + showTurboTopupModal(context, onSuccess: () { + onTurboTopupSucess(); + }); + }, + child: ArDriveClickArea( + child: RichText( + text: TextSpan( + children: [ + // TODO: use text with multiple styles + TextSpan( + text: 'Use Turbo Credits', // TODO: localize + style: ArDriveTypography.body + .buttonLargeBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeFgDefault, + ) + .copyWith( + decoration: TextDecoration.underline, + ), + ), + TextSpan( + text: + ' for faster uploads.', // TODO: localize + style: ArDriveTypography.body.buttonLargeBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeFgDefault, + ), + ), + ], + ), + ), + ), + ) + : null, + ) + ], + builder: (index, radioButton) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + radioButton, + Padding( + padding: const EdgeInsets.only(left: 24.0), + child: Text( + index == 0 + // TODO: localize + ? 'Wallet Balance: $arBalance AR' + : 'Turbo Balance: $turboCredits Credits', + style: ArDriveTypography.body.buttonNormalBold( + color: + ArDriveTheme.of(context).themeData.colors.themeFgMuted, + ), + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 52d8d7c215..bc499de7fc 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -7,6 +7,7 @@ import 'package:ardrive/blocs/upload/models/upload_file.dart'; import 'package:ardrive/blocs/upload/upload_file_checker.dart'; import 'package:ardrive/blocs/upload/upload_handles/file_v2_upload_handle.dart'; import 'package:ardrive/components/file_picker_modal.dart'; +import 'package:ardrive/components/payment_method_selector_widget.dart'; import 'package:ardrive/core/crypto/crypto.dart'; import 'package:ardrive/core/upload/cost_calculator.dart'; import 'package:ardrive/core/upload/uploader.dart'; @@ -24,7 +25,6 @@ import 'package:ardrive/utils/logger/logger.dart'; import 'package:ardrive/utils/upload_plan_utils.dart'; import 'package:ardrive_io/ardrive_io.dart'; import 'package:ardrive_ui/ardrive_ui.dart'; -import 'package:arweave/utils.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -342,7 +342,8 @@ class _UploadFormState extends State { _uploadMethod = state.uploadMethod; logger.d( - ' is button to upload enabled: ${state.isButtonToUploadEnabled}'); + ' is button to upload enabled: ${state.isButtonToUploadEnabled}', + ); final v2Files = state.uploadPlanForAR.fileV2UploadHandles.values .map((e) => e) @@ -495,130 +496,31 @@ class _UploadFormState extends State { height: 8, ), }, - if (!state.isFreeThanksToTurbo) ...[ - Text( - 'Payment method:', - style: ArDriveTypography.body.buttonLargeBold(), - ), - const SizedBox( - height: 8, - ), - ArDriveRadioButtonGroup( - size: 15, - onChanged: (index, value) { - switch (index) { - case 0: - if (value) { - context - .read() - .setUploadMethod(UploadMethod.ar); - } - break; - - case 1: - if (value) { - context - .read() - .setUploadMethod(UploadMethod.turbo); - } - break; - } + if (!state.isFreeThanksToTurbo) + PaymentMethodSelector( + uploadMethod: state.uploadMethod, + costEstimateTurbo: state.costEstimateTurbo, + costEstimateAr: state.costEstimateAr, + hasNoTurboBalance: state.isZeroBalance, + isTurboUploadPossible: state.isTurboUploadPossible, + arBalance: state.arBalance, + turboCredits: state.turboCredits, + onArSelect: () { + context + .read() + .setUploadMethod(UploadMethod.ar); + }, + onTurboSelect: () { + context + .read() + .setUploadMethod(UploadMethod.turbo); + }, + onTurboTopupSucess: () { + context.read().startUploadPreparation( + isRetryingToPayWithTurbo: true, + ); }, - options: [ - RadioButtonOptions( - value: state.uploadMethod == UploadMethod.ar, - // TODO: Localization - text: - 'Cost: ${winstonToAr(state.costEstimateAr.totalCost)} AR', - textStyle: ArDriveTypography.body.buttonLargeBold(), - ), - if (state.costEstimateTurbo != null && - state.isTurboUploadPossible) - RadioButtonOptions( - value: state.uploadMethod == UploadMethod.turbo, - // TODO: Localization - text: state.isZeroBalance - ? '' - : 'Cost: ${winstonToAr(state.costEstimateTurbo!.totalCost)} Credits', - textStyle: ArDriveTypography.body.buttonLargeBold(), - content: state.isZeroBalance - ? GestureDetector( - onTap: () { - showTurboModal(context, onSuccess: () { - context - .read() - .startUploadPreparation( - isRetryingToPayWithTurbo: true, - ); - }); - }, - child: ArDriveClickArea( - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'Use Turbo Credits', - style: ArDriveTypography.body - .buttonLargeBold( - color: - ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ) - .copyWith( - decoration: TextDecoration - .underline, - ), - ), - TextSpan( - text: ' for faster uploads.', - style: ArDriveTypography.body - .buttonLargeBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ), - ), - ], - ), - ), - ), - ) - : null, - ) - ], - builder: (index, radioButton) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - radioButton, - Padding( - padding: const EdgeInsets.only(left: 24.0), - child: Text( - index == 0 - ? 'Wallet Balance: ${state.arBalance} AR' - : 'Turbo Balance: ${state.turboCredits} Credits', - style: ArDriveTypography.body.buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgMuted, - ), - ), - ), - ], - ), - ), - const SizedBox( - height: 16, - ), - _getInsufficientBalanceMessage( - sufficentCreditsBalance: state.sufficentCreditsBalance, - sufficientArBalance: state.sufficientArBalance, ), - ] ], ), actions: [ @@ -857,7 +759,7 @@ class _UploadFormState extends State { sufficientArBalance) { return GestureDetector( onTap: () { - showTurboModal(context, onSuccess: () { + showTurboTopupModal(context, onSuccess: () { context.read().startUploadPreparation( isRetryingToPayWithTurbo: true, ); @@ -907,7 +809,7 @@ class _UploadFormState extends State { } else if (!sufficentCreditsBalance && !sufficientArBalance) { return GestureDetector( onTap: () { - showTurboModal(context, onSuccess: () { + showTurboTopupModal(context, onSuccess: () { context.read().startUploadPreparation( isRetryingToPayWithTurbo: true, ); diff --git a/lib/turbo/topup/components/turbo_balance_widget.dart b/lib/turbo/topup/components/turbo_balance_widget.dart index 8ca586a67b..e5d55e080a 100644 --- a/lib/turbo/topup/components/turbo_balance_widget.dart +++ b/lib/turbo/topup/components/turbo_balance_widget.dart @@ -59,7 +59,7 @@ class _TurboBalanceState extends State { borderRadius: 20, onPressed: () { showCookiePolicyConsentModal(context, (context) { - showTurboModal(context); + showTurboTopupModal(context); }); widget.onTapAddButton?.call(); diff --git a/lib/turbo/topup/views/topup_modal.dart b/lib/turbo/topup/views/topup_modal.dart index f2434310eb..10af47e78d 100644 --- a/lib/turbo/topup/views/topup_modal.dart +++ b/lib/turbo/topup/views/topup_modal.dart @@ -19,7 +19,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_stripe/flutter_stripe.dart'; -void showTurboModal(BuildContext context, {Function()? onSuccess}) { +void showTurboTopupModal(BuildContext context, {Function()? onSuccess}) { final activityTracker = context.read(); final sessionManager = TurboSessionManager(); @@ -281,7 +281,7 @@ class _TurboModalState extends State with TickerProviderStateMixin { Navigator.of(modalContext).pop(); Navigator.of(context).pop(); - showTurboModal(parentContext); + showTurboTopupModal(parentContext); }, ), ), From c944ac134bfdc267e7764d7672850903f5e9139f Mon Sep 17 00:00:00 2001 From: Mati Date: Tue, 12 Sep 2023 18:32:43 -0300 Subject: [PATCH 02/23] feat(snapshots): let snapshots be uploaded with Turbo PE-3173 --- .../create_snapshot_cubit.dart | 225 ++++++++++++++---- .../create_snapshot_state.dart | 75 +++++- lib/blocs/upload/models/upload_plan.dart | 1 + lib/blocs/upload/upload_cubit.dart | 4 +- lib/components/create_snapshot_dialog.dart | 63 ++--- lib/entities/snapshot_entity.dart | 18 ++ lib/services/arweave/arweave_service.dart | 5 +- 7 files changed, 311 insertions(+), 80 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 456a270290..e4690e7def 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -2,15 +2,22 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io' show BytesBuilder; +import 'package:ardrive/authentication/ardrive_auth.dart'; import 'package:ardrive/blocs/constants.dart'; import 'package:ardrive/blocs/profile/profile_cubit.dart'; +import 'package:ardrive/blocs/upload/upload_cubit.dart'; import 'package:ardrive/core/upload/cost_calculator.dart'; import 'package:ardrive/entities/entities.dart'; import 'package:ardrive/entities/snapshot_entity.dart'; import 'package:ardrive/entities/string_types.dart'; import 'package:ardrive/models/daos/daos.dart'; import 'package:ardrive/services/arweave/arweave.dart'; +import 'package:ardrive/services/config/app_config.dart'; import 'package:ardrive/services/pst/pst.dart'; +import 'package:ardrive/turbo/services/payment_service.dart'; +import 'package:ardrive/turbo/services/upload_service.dart'; +import 'package:ardrive/turbo/turbo.dart'; +import 'package:ardrive/turbo/utils/utils.dart'; import 'package:ardrive/utils/html/html_util.dart'; import 'package:ardrive/utils/logger/logger.dart'; import 'package:ardrive/utils/metadata_cache.dart'; @@ -18,7 +25,6 @@ import 'package:ardrive/utils/snapshots/height_range.dart'; import 'package:ardrive/utils/snapshots/range.dart'; import 'package:ardrive/utils/snapshots/snapshot_item_to_be_created.dart'; import 'package:arweave/arweave.dart'; -import 'package:arweave/utils.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -29,10 +35,15 @@ part 'create_snapshot_state.dart'; class CreateSnapshotCubit extends Cubit { final ArweaveService _arweave; + final PaymentService _paymentService; final DriveDao _driveDao; final ProfileCubit _profileCubit; final PstService _pst; final TabVisibilitySingleton _tabVisibility; + final TurboBalanceRetriever turboBalanceRetriever; + final ArDriveAuth auth; + final AppConfig appConfig; + final TurboUploadService turboService; @visibleForTesting bool throwOnDataComputingForTesting; @visibleForTesting @@ -44,7 +55,21 @@ class CreateSnapshotCubit extends Cubit { late String _ownerAddress; late Range _range; late int _currentHeight; - late Transaction _preparedTx; + Uint8List? data; + DataItem? _preparedDataItem; + Transaction? _preparedTx; + + UploadMethod _uploadMethod = UploadMethod.ar; + UploadCostEstimate _costEstimateAr = UploadCostEstimate.zero(); + UploadCostEstimate _costEstimateTurbo = UploadCostEstimate.zero(); + bool _hasNoTurboBalance = false; + String _arBalance = ''; + String _turboCredits = ''; + bool _isButtonToUploadEnabled = false; + bool _isTurboUploadPossible = true; + bool _sufficentCreditsBalance = false; + bool _sufficientArBalance = false; + bool _isFreeThanksToTurbo = false; bool _wasSnapshotDataComputingCanceled = false; @@ -53,14 +78,20 @@ class CreateSnapshotCubit extends Cubit { CreateSnapshotCubit({ required ArweaveService arweave, + required PaymentService paymentService, required ProfileCubit profileCubit, required DriveDao driveDao, required PstService pst, required TabVisibilitySingleton tabVisibility, + required this.turboBalanceRetriever, + required this.auth, + required this.appConfig, + required this.turboService, this.throwOnDataComputingForTesting = false, this.throwOnSignTxForTesting = false, this.returnWithoutSigningForTesting = false, }) : _arweave = arweave, + _paymentService = paymentService, _profileCubit = profileCubit, _driveDao = driveDao, _pst = pst, @@ -98,15 +129,11 @@ class CreateSnapshotCubit extends Cubit { _setupSnapshotEntityWithBlob(data); - await _prepareAndSignTx( - _snapshotEntity!, - data, - ); + await _computeCost(); - final costResult = await _computeCostAndCheckBalance(); - if (costResult == null) return; - - await _emitConfirming(costResult, data.length); + await _emitConfirming( + dataSize: data.length, + ); } bool _wasCancelled() { @@ -180,7 +207,7 @@ class CreateSnapshotCubit extends Cubit { Future _prepareAndSignTx( SnapshotEntity snapshotEntity, - Uint8List data, + // Uint8List data, ) async { logger.i('About to prepare and sign snapshot transaction'); @@ -191,10 +218,14 @@ class CreateSnapshotCubit extends Cubit { )); await prepareTx(isArConnectProfile); - await _pst.addCommunityTipToTx(_preparedTx); + // await _pst.addCommunityTipToTx(_preparedDataItem); // Turbo should be the one setting the tip, right? await signTx(isArConnectProfile); - snapshotEntity.txId = _preparedTx.id; + if (_uploadMethod == UploadMethod.ar) { + snapshotEntity.txId = _preparedTx!.id; + } else { + snapshotEntity.txId = _preparedDataItem!.id; + } } @visibleForTesting @@ -207,13 +238,29 @@ class CreateSnapshotCubit extends Cubit { 'Preparing snapshot transaction with ${isArConnectProfile ? 'ArConnect' : 'JSON wallet'}', ); - _preparedTx = await _arweave.prepareEntityTx( - _snapshotEntity!, - wallet, - null, - // We'll sign it just after adding the tip - skipSignature: true, - ); + if (_uploadMethod == UploadMethod.ar) { + _preparedTx = await _arweave.prepareEntityTx( + _snapshotEntity!, + wallet, + null, + // We'll sign it just after adding the tip + skipSignature: true, + ); + } else { + _preparedDataItem = await _arweave.prepareEntityDataItem( + _snapshotEntity!, + wallet, + // We'll sign it just after adding the tip + skipSignature: true, + ); + } + + // _preparedDataItem = await _arweave.prepareEntityDataItem( + // _snapshotEntity!, + // wallet, + // // We'll sign it just after adding the tip + // skipSignature: true, + // ); } catch (e) { final isTabFocused = _tabVisibility.isTabFocused(); if (isArConnectProfile && !isTabFocused) { @@ -249,7 +296,11 @@ class CreateSnapshotCubit extends Cubit { return; } - await _preparedTx.sign(wallet); + if (_uploadMethod == UploadMethod.ar) { + await _preparedTx!.sign(wallet); + } else { + await _preparedDataItem!.sign(wallet); + } } catch (e) { final isTabFocused = _tabVisibility.isTabFocused(); if (isArConnectProfile && !isTabFocused) { @@ -323,39 +374,112 @@ class CreateSnapshotCubit extends Cubit { _snapshotEntity = snapshotEntity; } - Future _computeCostAndCheckBalance() async { - final totalCost = _preparedTx.reward + _preparedTx.quantity; - - final profile = _profileCubit.state as ProfileLoggedIn; - final walletBalance = profile.walletBalance; + Future _computeCost() async { + final profileState = _profileCubit.state as ProfileLoggedIn; + final wallet = profileState.wallet; - if (walletBalance < totalCost) { - emit(CreateSnapshotInsufficientBalance( - walletBalance: walletBalance.toString(), - arCost: winstonToAr(totalCost), - )); - return null; - } + UploadCostEstimateCalculatorForAR costCalculatorForAr = + UploadCostEstimateCalculatorForAR( + arweaveService: _arweave, + pstService: _pst, + arCostToUsd: ConvertArToUSD(arweave: _arweave), + ); - return totalCost; - } + final turboCostCalc = TurboCostCalculator(paymentService: _paymentService); + TurboUploadCostCalculator costCalculatorForTurbo = + TurboUploadCostCalculator( + turboCostCalculator: turboCostCalc, + priceEstimator: TurboPriceEstimator( + wallet: wallet, + paymentService: _paymentService, + costCalculator: turboCostCalc, + ), + ); - Future _emitConfirming( - BigInt totalCost, - int dataSize, - ) async { - final arUploadCost = winstonToAr(totalCost); + _costEstimateAr = await costCalculatorForAr.calculateCost( + totalSize: _snapshotEntity!.data!.length, + ); + _costEstimateTurbo = await costCalculatorForTurbo.calculateCost( + totalSize: _snapshotEntity!.data!.length, + ); - final double? usdUploadCost = await ConvertArToUSD(arweave: _arweave) - .convertForUSD(double.parse(arUploadCost)); + final turboBalance = + await turboBalanceRetriever.getBalance(wallet).catchError((e) { + logger.e('Error while retrieving turbo balance', e); + return BigInt.zero; + }); + _hasNoTurboBalance = turboBalance == BigInt.zero; + _turboCredits = convertCreditsToLiteralString(turboBalance); + _arBalance = convertCreditsToLiteralString(auth.currentUser!.walletBalance); + + bool sufficientBalanceToPayWithAR = + profileState.walletBalance >= _costEstimateAr.totalCost; + bool sufficientBalanceToPayWithTurbo = + _costEstimateTurbo.totalCost <= turboBalance; + + _sufficientArBalance = sufficientBalanceToPayWithAR; + _sufficentCreditsBalance = sufficientBalanceToPayWithTurbo; + + bool isTurboEnabled = appConfig.useTurboUpload; + _isTurboUploadPossible = + isTurboEnabled && _costEstimateTurbo.totalCost <= turboBalance; + } + Future _emitConfirming({ + required int + dataSize, // TODO: pass the correct value depending on v2 vs dataitem + }) async { emit(ConfirmingSnapshotCreation( snapshotSize: dataSize, - arUploadCost: arUploadCost, - usdUploadCost: usdUploadCost, + costEstimateAr: _costEstimateAr, + costEstimateTurbo: _costEstimateTurbo, + hasNoTurboBalance: _hasNoTurboBalance, + isTurboUploadPossible: _isTurboUploadPossible, + arBalance: _arBalance, + turboCredits: _turboCredits, + uploadMethod: _uploadMethod, + isButtonToUploadEnabled: _isButtonToUploadEnabled, + sufficientBalanceToPayWithAr: _sufficientArBalance, + sufficientBalanceToPayWithTurbo: _sufficentCreditsBalance, + isFreeThanksToTurbo: _isFreeThanksToTurbo, )); } + void setUploadMethod(UploadMethod method) { + logger.d('Upload method set to $method'); + _uploadMethod = method; + + _refreshIsButtonEnabled(); + } + + void _refreshIsButtonEnabled() { + _isButtonToUploadEnabled = false; + if (state is ConfirmingSnapshotCreation) { + final stateAsConfirming = state as ConfirmingSnapshotCreation; + logger.d('Sufficient Balance To Pay With AR: $_sufficientArBalance'); + + if (_uploadMethod == UploadMethod.ar && _sufficientArBalance) { + logger.d('Enabling button for AR payment method'); + _isButtonToUploadEnabled = true; + } else if (_uploadMethod == UploadMethod.turbo && + stateAsConfirming.isTurboUploadPossible && + _sufficentCreditsBalance) { + logger.d('Enabling button for Turbo payment method'); + _isButtonToUploadEnabled = true; + } else if (stateAsConfirming.isFreeThanksToTurbo) { + logger.d('Enabling button for free upload using Turbo'); + _isButtonToUploadEnabled = true; + } else { + logger.d('Disabling button'); + } + + emit(stateAsConfirming.copyWith( + uploadMethod: _uploadMethod, + isButtonToUploadEnabled: _isButtonToUploadEnabled, + )); + } + } + Future _jsonMetadataOfTxId(String txId) async { final drive = await _driveDao.driveById(driveId: _driveId).getSingleOrNull(); @@ -396,10 +520,21 @@ class CreateSnapshotCubit extends Cubit { return; } + await _prepareAndSignTx( + _snapshotEntity!, + ); + try { emit(UploadingSnapshot()); - await _arweave.postTx(_preparedTx); + if (_uploadMethod == UploadMethod.ar) { + await _arweave.postTx(_preparedTx!); + } else { + turboService.postDataItem( + dataItem: _preparedDataItem!, + wallet: (_profileCubit.state as ProfileLoggedIn).wallet, + ); + } emit(SnapshotUploadSuccess()); } catch (err, stacktrace) { diff --git a/lib/blocs/create_snapshot/create_snapshot_state.dart b/lib/blocs/create_snapshot/create_snapshot_state.dart index dd63bcaa86..924b4ff13c 100644 --- a/lib/blocs/create_snapshot/create_snapshot_state.dart +++ b/lib/blocs/create_snapshot/create_snapshot_state.dart @@ -54,20 +54,85 @@ class CreateSnapshotInsufficientBalance extends CreateSnapshotState { class ConfirmingSnapshotCreation extends CreateSnapshotState { final int snapshotSize; - final String arUploadCost; - final double? usdUploadCost; + + final UploadCostEstimate costEstimateAr; + final UploadCostEstimate? costEstimateTurbo; + final bool hasNoTurboBalance; + final bool isTurboUploadPossible; + final String arBalance; + final String turboCredits; + final UploadMethod uploadMethod; + final bool isButtonToUploadEnabled; + final bool sufficientBalanceToPayWithAr; + final bool sufficientBalanceToPayWithTurbo; + final bool isFreeThanksToTurbo; ConfirmingSnapshotCreation({ required this.snapshotSize, - required this.arUploadCost, - required this.usdUploadCost, + required this.costEstimateAr, + required this.costEstimateTurbo, + required this.hasNoTurboBalance, + required this.isTurboUploadPossible, + required this.arBalance, + required this.turboCredits, + required this.uploadMethod, + required this.isButtonToUploadEnabled, + required this.sufficientBalanceToPayWithAr, + required this.sufficientBalanceToPayWithTurbo, + required this.isFreeThanksToTurbo, }); @override List get props => [ snapshotSize, - arUploadCost, + costEstimateAr, + costEstimateTurbo ?? '', + hasNoTurboBalance, + isTurboUploadPossible, + arBalance, + turboCredits, + uploadMethod, + isButtonToUploadEnabled, + sufficientBalanceToPayWithAr, + sufficientBalanceToPayWithTurbo, + isFreeThanksToTurbo, ]; + + ConfirmingSnapshotCreation copyWith({ + int? snapshotSize, + String? arUploadCost, + double? usdUploadCost, + UploadCostEstimate? costEstimateAr, + UploadCostEstimate? costEstimateTurbo, + bool? hasNoTurboBalance, + bool? isTurboUploadPossible, + String? arBalance, + String? turboCredits, + UploadMethod? uploadMethod, + bool? isButtonToUploadEnabled, + bool? sufficientBalanceToPayWithAr, + bool? sufficientBalanceToPayWithTurbo, + bool? isFreeThanksToTurbo, + }) { + return ConfirmingSnapshotCreation( + snapshotSize: snapshotSize ?? this.snapshotSize, + costEstimateAr: costEstimateAr ?? this.costEstimateAr, + costEstimateTurbo: costEstimateTurbo ?? this.costEstimateTurbo, + hasNoTurboBalance: hasNoTurboBalance ?? this.hasNoTurboBalance, + isTurboUploadPossible: + isTurboUploadPossible ?? this.isTurboUploadPossible, + arBalance: arBalance ?? this.arBalance, + turboCredits: turboCredits ?? this.turboCredits, + uploadMethod: uploadMethod ?? this.uploadMethod, + isButtonToUploadEnabled: + isButtonToUploadEnabled ?? this.isButtonToUploadEnabled, + sufficientBalanceToPayWithAr: + sufficientBalanceToPayWithAr ?? this.sufficientBalanceToPayWithAr, + sufficientBalanceToPayWithTurbo: sufficientBalanceToPayWithTurbo ?? + this.sufficientBalanceToPayWithTurbo, + isFreeThanksToTurbo: isFreeThanksToTurbo ?? this.isFreeThanksToTurbo, + ); + } } class UploadingSnapshot extends CreateSnapshotState {} diff --git a/lib/blocs/upload/models/upload_plan.dart b/lib/blocs/upload/models/upload_plan.dart index cc2f08509b..0e9f4aa877 100644 --- a/lib/blocs/upload/models/upload_plan.dart +++ b/lib/blocs/upload/models/upload_plan.dart @@ -28,6 +28,7 @@ class UploadPlan { required Map fileDataItemUploadHandles, required Map folderDataItemUploadHandles, + // required Map snapshotUploadHandles, required TurboUploadService turboUploadService, required int maxDataItemCount, }) async { diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index eae65f7968..dd1faea622 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -72,7 +72,9 @@ class UploadCubit extends Cubit { } emit((state as UploadReady).copyWith( - uploadMethod: method, isButtonToUploadEnabled: isButtonEnabled)); + uploadMethod: method, + isButtonToUploadEnabled: isButtonEnabled, + )); } } diff --git a/lib/components/create_snapshot_dialog.dart b/lib/components/create_snapshot_dialog.dart index 50f6d18d72..d883b8d716 100644 --- a/lib/components/create_snapshot_dialog.dart +++ b/lib/components/create_snapshot_dialog.dart @@ -1,11 +1,17 @@ +import 'package:ardrive/authentication/ardrive_auth.dart'; +import 'package:ardrive/blocs/blocs.dart'; import 'package:ardrive/blocs/create_snapshot/create_snapshot_cubit.dart'; -import 'package:ardrive/blocs/profile/profile_cubit.dart'; import 'package:ardrive/components/components.dart'; +import 'package:ardrive/components/payment_method_selector_widget.dart'; import 'package:ardrive/entities/string_types.dart'; import 'package:ardrive/models/models.dart'; import 'package:ardrive/services/arweave/arweave.dart'; +import 'package:ardrive/services/config/config.dart'; import 'package:ardrive/services/pst/pst.dart'; import 'package:ardrive/theme/theme.dart'; +import 'package:ardrive/turbo/services/payment_service.dart'; +import 'package:ardrive/turbo/services/upload_service.dart'; +import 'package:ardrive/turbo/turbo.dart'; import 'package:ardrive/utils/app_localizations_wrapper.dart'; import 'package:ardrive/utils/filesize.dart'; import 'package:ardrive/utils/html/html_util.dart'; @@ -15,8 +21,6 @@ import 'package:ardrive_ui/ardrive_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../utils/usd_upload_cost_to_string.dart'; - Future promptToCreateSnapshot( BuildContext context, Drive drive, @@ -31,6 +35,13 @@ Future promptToCreateSnapshot( profileCubit: context.read(), pst: context.read(), tabVisibility: TabVisibilitySingleton(), + auth: context.read(), + paymentService: context.read(), + turboBalanceRetriever: TurboBalanceRetriever( + paymentService: context.read(), + ), + appConfig: context.read().config, + turboService: context.read(), ), child: CreateSnapshotDialog( drive: drive, @@ -364,31 +375,6 @@ Widget _confirmDialog( ), style: ArDriveTypography.body.buttonNormalRegular(), ), - const Divider(), - const SizedBox(height: 16), - Text.rich( - TextSpan( - children: [ - TextSpan( - text: appLocalizationsOf(context).cost( - state.arUploadCost, - ), - ), - if (state.usdUploadCost != null) - TextSpan( - text: usdUploadCostToString( - state.usdUploadCost!, - ), - ) - else - TextSpan( - text: - ' ${appLocalizationsOf(context).usdPriceNotAvailable}', - ), - ], - style: ArDriveTypography.body.buttonNormalRegular(), - ), - ), Text.rich( TextSpan( children: [ @@ -401,6 +387,27 @@ Widget _confirmDialog( style: ArDriveTypography.body.buttonNormalRegular(), ), ), + const Divider(), + const SizedBox(height: 16), + // TODO: is free thanks to turbo + PaymentMethodSelector( + uploadMethod: state.uploadMethod, + costEstimateTurbo: state.costEstimateTurbo, + costEstimateAr: state.costEstimateAr, + hasNoTurboBalance: state.hasNoTurboBalance, + isTurboUploadPossible: true, + arBalance: state.arBalance, + turboCredits: state.turboCredits, + onTurboTopupSucess: () { + // TODO - + }, + onArSelect: () { + createSnapshotCubit.setUploadMethod(UploadMethod.ar); + }, + onTurboSelect: () { + createSnapshotCubit.setUploadMethod(UploadMethod.turbo); + }, + ), ], ), ), diff --git a/lib/entities/snapshot_entity.dart b/lib/entities/snapshot_entity.dart index 98d901bae7..5b03f7edcc 100644 --- a/lib/entities/snapshot_entity.dart +++ b/lib/entities/snapshot_entity.dart @@ -117,4 +117,22 @@ class SnapshotEntity extends Entity { return tx; } + + @override + Future asDataItem(SecretKey? key) async { + if (key != null) { + throw UnsupportedError('Snapshot entities are not encrypted.'); + } + + final item = DataItem.withBlobData(data: data!); + final packageInfo = await PackageInfo.fromPlatform(); + + item.addTag(EntityTag.contentType, ContentType.json); + addEntityTagsToTransaction(item); + item.addApplicationTags( + version: packageInfo.version, + ); + + return item; + } } diff --git a/lib/services/arweave/arweave_service.dart b/lib/services/arweave/arweave_service.dart index 02781e67c8..729e6c8628 100644 --- a/lib/services/arweave/arweave_service.dart +++ b/lib/services/arweave/arweave_service.dart @@ -920,11 +920,14 @@ class ArweaveService { Entity entity, Wallet wallet, { SecretKey? key, + bool skipSignature = false, }) async { final item = await entity.asDataItem(key); item.setOwner(await wallet.getOwner()); - await item.sign(wallet); + if (!skipSignature) { + await item.sign(wallet); + } return item; } From 96c167fad5849502bc5ded9dd3bf567ae93c3c43 Mon Sep 17 00:00:00 2001 From: Mati Date: Tue, 12 Sep 2023 18:39:22 -0300 Subject: [PATCH 03/23] feat(snapshots): missing await PE-3173 --- lib/blocs/create_snapshot/create_snapshot_cubit.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index e4690e7def..1a54205713 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -530,7 +530,7 @@ class CreateSnapshotCubit extends Cubit { if (_uploadMethod == UploadMethod.ar) { await _arweave.postTx(_preparedTx!); } else { - turboService.postDataItem( + await turboService.postDataItem( dataItem: _preparedDataItem!, wallet: (_profileCubit.state as ProfileLoggedIn).wallet, ); From 2c461a9c338bd30cecf315974804f6c485f7a56b Mon Sep 17 00:00:00 2001 From: Mati Date: Thu, 14 Sep 2023 16:54:45 -0300 Subject: [PATCH 04/23] feat(create snapshot cubit): free turbo uploads PE-3173 --- .../create_snapshot_cubit.dart | 60 +++++++++++++------ lib/components/create_snapshot_dialog.dart | 51 ++++++++++------ 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 1a54205713..82b296277d 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -65,6 +65,7 @@ class CreateSnapshotCubit extends Cubit { bool _hasNoTurboBalance = false; String _arBalance = ''; String _turboCredits = ''; + BigInt _turboBalance = BigInt.zero; bool _isButtonToUploadEnabled = false; bool _isTurboUploadPossible = true; bool _sufficentCreditsBalance = false; @@ -130,6 +131,10 @@ class CreateSnapshotCubit extends Cubit { _setupSnapshotEntityWithBlob(data); await _computeCost(); + await _computeBalanceEstimate(); + _computeIsSufficientBalance(); + _computeIsTurboEnabled(); + _computeIsFreeThanksToTurbo(); await _emitConfirming( dataSize: data.length, @@ -254,13 +259,6 @@ class CreateSnapshotCubit extends Cubit { skipSignature: true, ); } - - // _preparedDataItem = await _arweave.prepareEntityDataItem( - // _snapshotEntity!, - // wallet, - // // We'll sign it just after adding the tip - // skipSignature: true, - // ); } catch (e) { final isTabFocused = _tabVisibility.isTabFocused(); if (isArConnectProfile && !isTabFocused) { @@ -402,33 +400,49 @@ class CreateSnapshotCubit extends Cubit { _costEstimateTurbo = await costCalculatorForTurbo.calculateCost( totalSize: _snapshotEntity!.data!.length, ); + } + + Future _computeBalanceEstimate() async { + final profileState = _profileCubit.state as ProfileLoggedIn; + final wallet = profileState.wallet; final turboBalance = await turboBalanceRetriever.getBalance(wallet).catchError((e) { logger.e('Error while retrieving turbo balance', e); return BigInt.zero; }); + + _turboBalance = turboBalance; _hasNoTurboBalance = turboBalance == BigInt.zero; _turboCredits = convertCreditsToLiteralString(turboBalance); _arBalance = convertCreditsToLiteralString(auth.currentUser!.walletBalance); + } + + void _computeIsTurboEnabled() async { + bool isTurboEnabled = appConfig.useTurboUpload; + _isTurboUploadPossible = isTurboEnabled && _sufficentCreditsBalance; + } + + void _computeIsSufficientBalance() { + final profileState = _profileCubit.state as ProfileLoggedIn; bool sufficientBalanceToPayWithAR = profileState.walletBalance >= _costEstimateAr.totalCost; bool sufficientBalanceToPayWithTurbo = - _costEstimateTurbo.totalCost <= turboBalance; + _costEstimateTurbo.totalCost <= _turboBalance; _sufficientArBalance = sufficientBalanceToPayWithAR; _sufficentCreditsBalance = sufficientBalanceToPayWithTurbo; + } - bool isTurboEnabled = appConfig.useTurboUpload; - _isTurboUploadPossible = - isTurboEnabled && _costEstimateTurbo.totalCost <= turboBalance; + void _computeIsFreeThanksToTurbo() { + final allowedDataItemSizeForTurbo = appConfig.allowedDataItemSizeForTurbo; + final isFreeThanksToTurbo = + _snapshotEntity!.data!.length <= allowedDataItemSizeForTurbo; + _isFreeThanksToTurbo = isFreeThanksToTurbo; } - Future _emitConfirming({ - required int - dataSize, // TODO: pass the correct value depending on v2 vs dataitem - }) async { + Future _emitConfirming({required int dataSize}) async { emit(ConfirmingSnapshotCreation( snapshotSize: dataSize, costEstimateAr: _costEstimateAr, @@ -530,9 +544,8 @@ class CreateSnapshotCubit extends Cubit { if (_uploadMethod == UploadMethod.ar) { await _arweave.postTx(_preparedTx!); } else { - await turboService.postDataItem( + await _postTurboDataItem( dataItem: _preparedDataItem!, - wallet: (_profileCubit.state as ProfileLoggedIn).wallet, ); } @@ -543,6 +556,19 @@ class CreateSnapshotCubit extends Cubit { } } + Future _postTurboDataItem({required DataItem dataItem}) async { + final profile = _profileCubit.state as ProfileLoggedIn; + final wallet = profile.wallet; + + final addr = await wallet.getAddress(); + logger.d('Posting snapshot transaction with $addr'); + + await turboService.postDataItem( + dataItem: dataItem, + wallet: wallet, + ); + } + void cancelSnapshotCreation() { logger.i('User cancelled the snapshot creation'); diff --git a/lib/components/create_snapshot_dialog.dart b/lib/components/create_snapshot_dialog.dart index d883b8d716..e653d6ebcd 100644 --- a/lib/components/create_snapshot_dialog.dart +++ b/lib/components/create_snapshot_dialog.dart @@ -389,25 +389,38 @@ Widget _confirmDialog( ), const Divider(), const SizedBox(height: 16), - // TODO: is free thanks to turbo - PaymentMethodSelector( - uploadMethod: state.uploadMethod, - costEstimateTurbo: state.costEstimateTurbo, - costEstimateAr: state.costEstimateAr, - hasNoTurboBalance: state.hasNoTurboBalance, - isTurboUploadPossible: true, - arBalance: state.arBalance, - turboCredits: state.turboCredits, - onTurboTopupSucess: () { - // TODO - - }, - onArSelect: () { - createSnapshotCubit.setUploadMethod(UploadMethod.ar); - }, - onTurboSelect: () { - createSnapshotCubit.setUploadMethod(UploadMethod.turbo); - }, - ), + if (state.isFreeThanksToTurbo) ...{ + Text( + appLocalizationsOf(context).freeTurboTransaction, + style: ArDriveTypography.body.buttonNormalRegular( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeFgDefault, + ), + ), + } else ...{ + PaymentMethodSelector( + uploadMethod: state.uploadMethod, + costEstimateTurbo: state.costEstimateTurbo, + costEstimateAr: state.costEstimateAr, + hasNoTurboBalance: state.hasNoTurboBalance, + isTurboUploadPossible: true, + arBalance: state.arBalance, + turboCredits: state.turboCredits, + onTurboTopupSucess: () { + createSnapshotCubit + .setUploadMethod(UploadMethod.turbo); + }, + onArSelect: () { + createSnapshotCubit.setUploadMethod(UploadMethod.ar); + }, + onTurboSelect: () { + createSnapshotCubit + .setUploadMethod(UploadMethod.turbo); + }, + ), + } ], ), ), From 598c77efe3d8da4c10868a4b5a0d312da0937b61 Mon Sep 17 00:00:00 2001 From: Mati Date: Thu, 14 Sep 2023 17:15:27 -0300 Subject: [PATCH 05/23] feat(upload form): adds back the call to _getInsufficientBalanceMessage PE-3173 --- lib/components/upload_form.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index bc499de7fc..489bf5f454 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -521,6 +521,13 @@ class _UploadFormState extends State { ); }, ), + const SizedBox( + height: 16, + ), + _getInsufficientBalanceMessage( + sufficientArBalance: state.sufficientArBalance, + sufficentCreditsBalance: state.sufficentCreditsBalance, + ), ], ), actions: [ From 546900d78429e76104372d81114296a7dfa0597a Mon Sep 17 00:00:00 2001 From: Mati Date: Thu, 14 Sep 2023 17:44:01 -0300 Subject: [PATCH 06/23] test(create snapshot): fixes nbronken tests PE-3173 --- test/blocs/create_snapshot_cubit_test.dart | 107 +++++++++++++++++++-- 1 file changed, 100 insertions(+), 7 deletions(-) diff --git a/test/blocs/create_snapshot_cubit_test.dart b/test/blocs/create_snapshot_cubit_test.dart index d844bcb7e3..0a5be4f2b5 100644 --- a/test/blocs/create_snapshot_cubit_test.dart +++ b/test/blocs/create_snapshot_cubit_test.dart @@ -1,6 +1,12 @@ import 'package:ardrive/blocs/create_snapshot/create_snapshot_cubit.dart'; import 'package:ardrive/blocs/profile/profile_cubit.dart'; +import 'package:ardrive/entities/profile_types.dart'; import 'package:ardrive/entities/snapshot_entity.dart'; +import 'package:ardrive/services/config/app_config.dart'; +import 'package:ardrive/turbo/services/payment_service.dart'; +import 'package:ardrive/turbo/services/upload_service.dart'; +import 'package:ardrive/types/winston.dart'; +import 'package:ardrive/user/user.dart'; import 'package:ardrive/utils/snapshots/range.dart'; import 'package:arweave/arweave.dart'; import 'package:arweave/utils.dart'; @@ -11,6 +17,7 @@ import 'package:mocktail/mocktail.dart'; import 'package:package_info_plus/package_info_plus.dart'; import '../test_utils/utils.dart'; +import '../turbo/turbo_test.dart'; Future fakePrepareTransaction(invocation) async { final entity = invocation.positionalArguments[0] as SnapshotEntity; @@ -28,6 +35,10 @@ Future fakePrepareTransaction(invocation) async { return transaction; } +class MockAppConfig extends Mock implements AppConfig {} + +class MockTurboUploadService extends Mock implements TurboUploadService {} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -40,6 +51,11 @@ void main() { final pst = MockPstService(); final tabVisibility = MockTabVisibilitySingleton(); final testWallet = getTestWallet(); + final appConfig = MockAppConfig(); + final auth = MockArDriveAuth(); + final paymentService = MockPaymentService(); + final turboBalanceRetriever = MockTurboBalanceRetriever(); + final turboService = MockTurboUploadService(); setUpAll(() async { registerFallbackValue(SnapshotEntity()); @@ -51,7 +67,8 @@ void main() { }); setUp(() async { - // mocks the getSegmentedTransactionsFromDrive method of ardrive + registerFallbackValue(BigInt.one); + when( () => arweave.getSegmentedTransactionsFromDrive( any(), @@ -125,6 +142,42 @@ void main() { (_) => Future.value(stubArToUsdFactor), ); + when(() => arweave.getPrice(byteSize: any(named: 'byteSize'))) + .thenAnswer((invocation) async => BigInt.one); + + when(() => pst.getPSTFee(any())) + .thenAnswer((invocation) async => Winston(BigInt.one)); + + when(() => paymentService.getPriceForBytes( + byteSize: any(named: 'byteSize'))) + .thenAnswer((invocation) async => BigInt.one); + + when(() => paymentService.getPriceForFiat( + wallet: null, + amount: any(named: 'amount'), + currency: any(named: 'currency'), + )).thenAnswer((invocation) async => PriceForFiat.zero()); + + when(() => turboBalanceRetriever.getBalance(any())) + .thenAnswer((invocation) async => BigInt.one); + + final MockWallet wallet = MockWallet(); + const address = 'addr'; + final cipher = SecretKey([1, 2, 3, 4, 5]); + + when(() => auth.currentUser).thenAnswer((invocation) => User( + password: 'password', + wallet: wallet, + walletAddress: address, + walletBalance: BigInt.one, + cipherKey: cipher, + profileType: ProfileType.json, + )); + + when(() => appConfig.allowedDataItemSizeForTurbo) + .thenAnswer((invocation) => 100); + when(() => appConfig.useTurboUpload).thenAnswer((invocation) => true); + // mocks PackageInfo PackageInfo.setMockInitialValues( appName: 'appName', @@ -143,6 +196,11 @@ void main() { driveDao: driveDao, tabVisibility: tabVisibility, pst: pst, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), expect: () => [], ); @@ -155,6 +213,11 @@ void main() { driveDao: driveDao, tabVisibility: tabVisibility, pst: pst, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) => cubit.confirmDriveAndHeighRange( 'driveId', @@ -165,8 +228,6 @@ void main() { driveId: 'driveId', range: Range(start: 0, end: 1), ), - PreparingAndSigningTransaction(isArConnectProfile: false), - // can't check for the actual value because it contains a signed transaction isA(), ], ); @@ -179,6 +240,11 @@ void main() { driveDao: driveDao, tabVisibility: tabVisibility, pst: pst, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) => cubit .confirmDriveAndHeighRange( @@ -191,9 +257,8 @@ void main() { driveId: 'driveId', range: Range(start: 0, end: 1), ), - PreparingAndSigningTransaction(isArConnectProfile: false), - // can't check for the actual value because it contains a signed transaction isA(), + PreparingAndSigningTransaction(isArConnectProfile: false), UploadingSnapshot(), SnapshotUploadSuccess(), ], @@ -208,6 +273,11 @@ void main() { tabVisibility: tabVisibility, pst: pst, throwOnDataComputingForTesting: true, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) => cubit.confirmDriveAndHeighRange( 'driveId', @@ -230,6 +300,11 @@ void main() { driveDao: driveDao, tabVisibility: tabVisibility, pst: pst, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) => cubit.confirmDriveAndHeighRange( 'driveId', @@ -240,7 +315,6 @@ void main() { driveId: 'driveId', range: Range(start: 0, end: 85), ), - PreparingAndSigningTransaction(isArConnectProfile: false), isA(), ], ); @@ -254,6 +328,11 @@ void main() { tabVisibility: tabVisibility, pst: pst, throwOnDataComputingForTesting: true, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) => cubit.confirmDriveAndHeighRange( 'driveId', @@ -276,6 +355,11 @@ void main() { driveDao: driveDao, tabVisibility: tabVisibility, pst: pst, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) async { await Future.wait([ @@ -355,6 +439,11 @@ void main() { driveDao: driveDao, tabVisibility: tabVisibility, pst: pst, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) async { await cubit.confirmDriveAndHeighRange( @@ -367,7 +456,6 @@ void main() { driveId: 'driveId', range: Range(start: 0, end: 1), ), - PreparingAndSigningTransaction(isArConnectProfile: true), isA(), ], ); @@ -381,6 +469,11 @@ void main() { tabVisibility: tabVisibility, pst: pst, throwOnSignTxForTesting: true, + auth: auth, + appConfig: appConfig, + paymentService: paymentService, + turboBalanceRetriever: turboBalanceRetriever, + turboService: turboService, ), act: (cubit) async { Future.delayed(const Duration(milliseconds: 8)).then((_) { From ea9f1c7dc4ccc8b3d728069b935bb83e149732c6 Mon Sep 17 00:00:00 2001 From: Mati Date: Fri, 22 Sep 2023 12:15:12 -0300 Subject: [PATCH 07/23] chore(create snapshot cubit & upload plan): cleanup PE-3173 --- lib/blocs/create_snapshot/create_snapshot_cubit.dart | 5 +---- lib/blocs/upload/models/upload_plan.dart | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 82b296277d..edb04a2148 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -212,7 +212,6 @@ class CreateSnapshotCubit extends Cubit { Future _prepareAndSignTx( SnapshotEntity snapshotEntity, - // Uint8List data, ) async { logger.i('About to prepare and sign snapshot transaction'); @@ -223,7 +222,6 @@ class CreateSnapshotCubit extends Cubit { )); await prepareTx(isArConnectProfile); - // await _pst.addCommunityTipToTx(_preparedDataItem); // Turbo should be the one setting the tip, right? await signTx(isArConnectProfile); if (_uploadMethod == UploadMethod.ar) { @@ -560,8 +558,7 @@ class CreateSnapshotCubit extends Cubit { final profile = _profileCubit.state as ProfileLoggedIn; final wallet = profile.wallet; - final addr = await wallet.getAddress(); - logger.d('Posting snapshot transaction with $addr'); + logger.d('Posting snapshot transaction for drive $_driveId'); await turboService.postDataItem( dataItem: dataItem, diff --git a/lib/blocs/upload/models/upload_plan.dart b/lib/blocs/upload/models/upload_plan.dart index 0e9f4aa877..cc2f08509b 100644 --- a/lib/blocs/upload/models/upload_plan.dart +++ b/lib/blocs/upload/models/upload_plan.dart @@ -28,7 +28,6 @@ class UploadPlan { required Map fileDataItemUploadHandles, required Map folderDataItemUploadHandles, - // required Map snapshotUploadHandles, required TurboUploadService turboUploadService, required int maxDataItemCount, }) async { From 6636abd6124abe9efc237612ddd410e86f4536b4 Mon Sep 17 00:00:00 2001 From: Mati Date: Mon, 25 Sep 2023 14:03:17 -0300 Subject: [PATCH 08/23] feat(new button): makes the option for snaqpshots be enabled even when ar is not enough to upload PE-4665 --- lib/components/new_button/new_button.dart | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/components/new_button/new_button.dart b/lib/components/new_button/new_button.dart index ea28b3bc80..cd2475c810 100644 --- a/lib/components/new_button/new_button.dart +++ b/lib/components/new_button/new_button.dart @@ -304,8 +304,7 @@ class NewButton extends StatelessWidget { icon: ArDriveIcons.iconAttachDrive(size: defaultIconSize), ), if (driveDetailState is DriveDetailLoadSuccess && drive != null) ...[ - if (driveDetailState.currentDrive.privacy == 'public' && - drive != null) + if (driveDetailState.currentDrive.privacy == 'public') ArDriveNewButtonItem( onClick: () { promptToCreateManifest( @@ -319,8 +318,7 @@ class NewButton extends StatelessWidget { name: appLocalizations.createManifest, icon: ArDriveIcons.tournament(size: defaultIconSize), ), - if (context.read().config.enableQuickSyncAuthoring && - drive != null) + if (context.read().config.enableQuickSyncAuthoring) ArDriveNewButtonItem( onClick: () { promptToCreateSnapshot( @@ -329,10 +327,7 @@ class NewButton extends StatelessWidget { ); }, isDisabled: !driveDetailState.hasWritePermissions || - driveDetailState.driveIsEmpty || - !profile.hasMinimumBalanceForUpload( - minimumWalletBalance: minimumWalletBalance, - ), + driveDetailState.driveIsEmpty, name: appLocalizations.createSnapshot, icon: ArDriveIcons.iconCreateSnapshot(size: defaultIconSize), ), From 91b078bddfa598d70d94539cfac2580bf7c7f590 Mon Sep 17 00:00:00 2001 From: Mati Date: Tue, 26 Sep 2023 17:15:54 -0300 Subject: [PATCH 09/23] feat(snapshot & upload): makes no balance message be part of payment method selector and not to be shown when turbo free uploads; makes the button be disabled when no balance PE-4676 --- .../create_snapshot_cubit.dart | 50 +++--- lib/components/create_snapshot_dialog.dart | 5 + .../payment_method_selector_widget.dart | 121 ++++++++++++- lib/components/upload_form.dart | 164 +++--------------- 4 files changed, 175 insertions(+), 165 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index edb04a2148..2133d43a16 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -135,6 +135,7 @@ class CreateSnapshotCubit extends Cubit { _computeIsSufficientBalance(); _computeIsTurboEnabled(); _computeIsFreeThanksToTurbo(); + _computeIsButtonEnabled(); await _emitConfirming( dataSize: data.length, @@ -461,34 +462,35 @@ class CreateSnapshotCubit extends Cubit { logger.d('Upload method set to $method'); _uploadMethod = method; - _refreshIsButtonEnabled(); + _computeIsButtonEnabled(); + if (state is ConfirmingSnapshotCreation) { + final stateAsConfirming = state as ConfirmingSnapshotCreation; + emit( + stateAsConfirming.copyWith( + uploadMethod: method, + isButtonToUploadEnabled: _isButtonToUploadEnabled, + ), + ); + } } - void _refreshIsButtonEnabled() { + void _computeIsButtonEnabled() { _isButtonToUploadEnabled = false; - if (state is ConfirmingSnapshotCreation) { - final stateAsConfirming = state as ConfirmingSnapshotCreation; - logger.d('Sufficient Balance To Pay With AR: $_sufficientArBalance'); - - if (_uploadMethod == UploadMethod.ar && _sufficientArBalance) { - logger.d('Enabling button for AR payment method'); - _isButtonToUploadEnabled = true; - } else if (_uploadMethod == UploadMethod.turbo && - stateAsConfirming.isTurboUploadPossible && - _sufficentCreditsBalance) { - logger.d('Enabling button for Turbo payment method'); - _isButtonToUploadEnabled = true; - } else if (stateAsConfirming.isFreeThanksToTurbo) { - logger.d('Enabling button for free upload using Turbo'); - _isButtonToUploadEnabled = true; - } else { - logger.d('Disabling button'); - } - emit(stateAsConfirming.copyWith( - uploadMethod: _uploadMethod, - isButtonToUploadEnabled: _isButtonToUploadEnabled, - )); + logger.d('Sufficient Balance To Pay With AR: $_sufficientArBalance'); + if (_uploadMethod == UploadMethod.ar && _sufficientArBalance) { + logger.d('Enabling button for AR payment method'); + _isButtonToUploadEnabled = true; + } else if (_uploadMethod == UploadMethod.turbo && + _isTurboUploadPossible && + _sufficentCreditsBalance) { + logger.d('Enabling button for Turbo payment method'); + _isButtonToUploadEnabled = true; + } else if (_isFreeThanksToTurbo) { + logger.d('Enabling button for free upload using Turbo'); + _isButtonToUploadEnabled = true; + } else { + logger.d('Disabling button'); } } diff --git a/lib/components/create_snapshot_dialog.dart b/lib/components/create_snapshot_dialog.dart index e653d6ebcd..a3a7a40c18 100644 --- a/lib/components/create_snapshot_dialog.dart +++ b/lib/components/create_snapshot_dialog.dart @@ -407,7 +407,11 @@ Widget _confirmDialog( hasNoTurboBalance: state.hasNoTurboBalance, isTurboUploadPossible: true, arBalance: state.arBalance, + sufficientArBalance: state.sufficientBalanceToPayWithAr, turboCredits: state.turboCredits, + sufficentCreditsBalance: + state.sufficientBalanceToPayWithTurbo, + isFreeThanksToTurbo: false, onTurboTopupSucess: () { createSnapshotCubit .setUploadMethod(UploadMethod.turbo); @@ -444,6 +448,7 @@ Widget _confirmDialog( await createSnapshotCubit.confirmSnapshotCreation(), }, title: appLocalizationsOf(context).uploadEmphasized, + isEnable: state.isButtonToUploadEnabled, ), } ], diff --git a/lib/components/payment_method_selector_widget.dart b/lib/components/payment_method_selector_widget.dart index 49f577e5d5..cc188de72d 100644 --- a/lib/components/payment_method_selector_widget.dart +++ b/lib/components/payment_method_selector_widget.dart @@ -12,7 +12,10 @@ class PaymentMethodSelector extends StatelessWidget { final bool hasNoTurboBalance; final bool isTurboUploadPossible; final String arBalance; + final bool sufficientArBalance; final String turboCredits; + final bool sufficentCreditsBalance; + final bool isFreeThanksToTurbo; final void Function() onTurboTopupSucess; final void Function() onArSelect; final void Function() onTurboSelect; @@ -25,7 +28,10 @@ class PaymentMethodSelector extends StatelessWidget { required this.hasNoTurboBalance, required this.isTurboUploadPossible, required this.arBalance, + required this.sufficientArBalance, required this.turboCredits, + required this.sufficentCreditsBalance, + required this.isFreeThanksToTurbo, required this.onTurboTopupSucess, required this.onArSelect, required this.onTurboSelect, @@ -33,7 +39,15 @@ class PaymentMethodSelector extends StatelessWidget { @override Widget build(context) { - return _buildContent(context); + return Column( + children: [ + if (!isFreeThanksToTurbo) ...[ + _buildContent(context), + const SizedBox(height: 16), + _getInsufficientBalanceMessage(context: context), + ], + ], + ); } Widget _buildContent(BuildContext context) { @@ -146,4 +160,109 @@ class PaymentMethodSelector extends StatelessWidget { ], ); } + + Widget _getInsufficientBalanceMessage({ + required BuildContext context, + }) { + if (uploadMethod == UploadMethod.turbo && + !sufficentCreditsBalance && + sufficientArBalance) { + return GestureDetector( + onTap: () { + showTurboTopupModal(context, onSuccess: () { + onTurboTopupSucess(); + }); + }, + child: ArDriveClickArea( + child: Text.rich( + TextSpan( + text: 'Insufficient Credit balance for purchase. ', + style: ArDriveTypography.body.captionBold( + color: + ArDriveTheme.of(context).themeData.colors.themeErrorDefault, + ), + children: [ + TextSpan( + text: 'Add Credits', + style: ArDriveTypography.body + .captionBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeErrorDefault, + ) + .copyWith(decoration: TextDecoration.underline), + ), + TextSpan( + text: ' to use Turbo.', + style: ArDriveTypography.body.captionBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeErrorDefault, + ), + ), + ], + ), + ), + ), + ); + } else if (uploadMethod == UploadMethod.ar && !sufficientArBalance) { + return Text( + 'Insufficient AR balance for purchase.', + style: ArDriveTypography.body.captionBold( + color: ArDriveTheme.of(context).themeData.colors.themeErrorDefault, + ), + ); + } else if (!sufficentCreditsBalance && !sufficientArBalance) { + return GestureDetector( + onTap: () { + showTurboTopupModal(context, onSuccess: () { + onTurboTopupSucess(); + }); + }, + child: ArDriveClickArea( + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: + 'Insufficient balance to pay for this upload. You can either', + style: ArDriveTypography.body.captionBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeErrorDefault, + ), + ), + TextSpan( + text: ' add Turbo credits to your profile', + style: ArDriveTypography.body + .captionBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeErrorDefault, + ) + .copyWith( + decoration: TextDecoration.underline, + ), + ), + TextSpan( + text: ' or use AR', + style: ArDriveTypography.body.captionBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeErrorDefault, + ), + ), + ], + ), + ), + ), + ); + } + return const SizedBox(); + } } diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 489bf5f454..82f7186e64 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -17,7 +17,6 @@ import 'package:ardrive/services/services.dart'; import 'package:ardrive/theme/theme.dart'; import 'package:ardrive/turbo/services/payment_service.dart'; import 'package:ardrive/turbo/services/upload_service.dart'; -import 'package:ardrive/turbo/topup/views/topup_modal.dart'; import 'package:ardrive/turbo/turbo.dart'; import 'package:ardrive/utils/app_localizations_wrapper.dart'; import 'package:ardrive/utils/filesize.dart'; @@ -496,37 +495,32 @@ class _UploadFormState extends State { height: 8, ), }, - if (!state.isFreeThanksToTurbo) - PaymentMethodSelector( - uploadMethod: state.uploadMethod, - costEstimateTurbo: state.costEstimateTurbo, - costEstimateAr: state.costEstimateAr, - hasNoTurboBalance: state.isZeroBalance, - isTurboUploadPossible: state.isTurboUploadPossible, - arBalance: state.arBalance, - turboCredits: state.turboCredits, - onArSelect: () { - context - .read() - .setUploadMethod(UploadMethod.ar); - }, - onTurboSelect: () { - context - .read() - .setUploadMethod(UploadMethod.turbo); - }, - onTurboTopupSucess: () { - context.read().startUploadPreparation( - isRetryingToPayWithTurbo: true, - ); - }, - ), - const SizedBox( - height: 16, - ), - _getInsufficientBalanceMessage( + PaymentMethodSelector( + uploadMethod: state.uploadMethod, + costEstimateTurbo: state.costEstimateTurbo, + costEstimateAr: state.costEstimateAr, + hasNoTurboBalance: state.isZeroBalance, + isTurboUploadPossible: state.isTurboUploadPossible, + arBalance: state.arBalance, sufficientArBalance: state.sufficientArBalance, + turboCredits: state.turboCredits, sufficentCreditsBalance: state.sufficentCreditsBalance, + isFreeThanksToTurbo: state.isFreeThanksToTurbo, + onArSelect: () { + context + .read() + .setUploadMethod(UploadMethod.ar); + }, + onTurboSelect: () { + context + .read() + .setUploadMethod(UploadMethod.turbo); + }, + onTurboTopupSucess: () { + context.read().startUploadPreparation( + isRetryingToPayWithTurbo: true, + ); + }, ), ], ), @@ -756,114 +750,4 @@ class _UploadFormState extends State { return const SizedBox(); }, ); - - Widget _getInsufficientBalanceMessage({ - required bool sufficientArBalance, - required bool sufficentCreditsBalance, - }) { - if (_uploadMethod == UploadMethod.turbo && - !sufficentCreditsBalance && - sufficientArBalance) { - return GestureDetector( - onTap: () { - showTurboTopupModal(context, onSuccess: () { - context.read().startUploadPreparation( - isRetryingToPayWithTurbo: true, - ); - }); - }, - child: ArDriveClickArea( - child: Text.rich( - TextSpan( - text: 'Insufficient Credit balance for purchase. ', - style: ArDriveTypography.body.captionBold( - color: - ArDriveTheme.of(context).themeData.colors.themeErrorDefault, - ), - children: [ - TextSpan( - text: 'Add Credits', - style: ArDriveTypography.body - .captionBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeErrorDefault, - ) - .copyWith(decoration: TextDecoration.underline), - ), - TextSpan( - text: ' to use Turbo.', - style: ArDriveTypography.body.captionBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeErrorDefault, - ), - ), - ], - ), - ), - ), - ); - } else if (_uploadMethod == UploadMethod.ar && !sufficientArBalance) { - return Text( - 'Insufficient AR balance for purchase.', - style: ArDriveTypography.body.captionBold( - color: ArDriveTheme.of(context).themeData.colors.themeErrorDefault, - ), - ); - } else if (!sufficentCreditsBalance && !sufficientArBalance) { - return GestureDetector( - onTap: () { - showTurboTopupModal(context, onSuccess: () { - context.read().startUploadPreparation( - isRetryingToPayWithTurbo: true, - ); - }); - }, - child: ArDriveClickArea( - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: - 'Insufficient balance to pay for this upload. You can either', - style: ArDriveTypography.body.captionBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeErrorDefault, - ), - ), - TextSpan( - text: ' add Turbo credits to your profile', - style: ArDriveTypography.body - .captionBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeErrorDefault, - ) - .copyWith( - decoration: TextDecoration.underline, - ), - ), - TextSpan( - text: ' or use AR', - style: ArDriveTypography.body.captionBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeErrorDefault, - ), - ), - ], - ), - ), - ), - ); - } - return const SizedBox(); - } } From 1a2e618fe3609c9e27b21dd8e59dae05f605ab28 Mon Sep 17 00:00:00 2001 From: Mati Date: Tue, 26 Sep 2023 17:49:04 -0300 Subject: [PATCH 10/23] feat(upload cubit): makes the file upload modal show the correct not enough balance message PE-4676 --- lib/blocs/upload/upload_cubit.dart | 2 +- lib/blocs/upload/upload_state.dart | 31 ++++++++++++++++++++++++++++++ lib/components/upload_form.dart | 3 --- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index dd1faea622..84e28317f1 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -71,7 +71,7 @@ class UploadCubit extends Cubit { logger.d('Disabling button'); } - emit((state as UploadReady).copyWith( + emit(uploadReady.copyWith( uploadMethod: method, isButtonToUploadEnabled: isButtonEnabled, )); diff --git a/lib/blocs/upload/upload_state.dart b/lib/blocs/upload/upload_state.dart index 0016b3a182..d3096eeea8 100644 --- a/lib/blocs/upload/upload_state.dart +++ b/lib/blocs/upload/upload_state.dart @@ -155,11 +155,42 @@ class UploadReady extends UploadState { @override List get props => [ costEstimateAr, + costEstimateTurbo, sufficientArBalance, + isZeroBalance, + sufficentCreditsBalance, + uploadIsPublic, uploadPlanForAR, + uploadPlanForTurbo, + isTurboUploadPossible, isFreeThanksToTurbo, + uploadSize, + credits, + arBalance, + turboCredits, + uploadMethod, isButtonToUploadEnabled, ]; + + @override + toString() => 'UploadReady { ' + 'costEstimateAr: $costEstimateAr, ' + 'costEstimateTurbo: $costEstimateTurbo, ' + 'sufficientArBalance: $sufficientArBalance, ' + 'isZeroBalance: $isZeroBalance, ' + 'sufficentCreditsBalance: $sufficentCreditsBalance, ' + 'uploadIsPublic: $uploadIsPublic, ' + 'uploadPlanForAR: $uploadPlanForAR, ' + 'uploadPlanForTurbo: $uploadPlanForTurbo, ' + 'isTurboUploadPossible: $isTurboUploadPossible, ' + 'isFreeThanksToTurbo: $isFreeThanksToTurbo, ' + 'uploadSize: $uploadSize, ' + 'credits: $credits, ' + 'arBalance: $arBalance, ' + 'turboCredits: $turboCredits, ' + 'uploadMethod: $uploadMethod, ' + 'isButtonToUploadEnabled: $isButtonToUploadEnabled, ' + '}'; } class UploadInProgress extends UploadState { diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 82f7186e64..92b89c825b 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -135,7 +135,6 @@ class UploadForm extends StatefulWidget { class _UploadFormState extends State { final _scrollController = ScrollController(); - UploadMethod? _uploadMethod; @override Widget build(BuildContext context) => BlocConsumer( @@ -338,8 +337,6 @@ class _UploadFormState extends State { final numberOfV2Files = state.uploadPlanForAR.fileV2UploadHandles.length; - _uploadMethod = state.uploadMethod; - logger.d( ' is button to upload enabled: ${state.isButtonToUploadEnabled}', ); From 405149c70c26fd176ed8f286c18b1f5ac01a1dca Mon Sep 17 00:00:00 2001 From: Mati Date: Wed, 27 Sep 2023 12:05:09 -0300 Subject: [PATCH 11/23] feat(create snapshot cubit): let the snapshot use turbo uploads when free thanks to turbo PE-4687 --- .../create_snapshot_cubit.dart | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 2133d43a16..8de333c7e9 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -72,6 +72,9 @@ class CreateSnapshotCubit extends Cubit { bool _sufficientArBalance = false; bool _isFreeThanksToTurbo = false; + bool get _useTruboUpload => + _uploadMethod == UploadMethod.turbo || _isFreeThanksToTurbo; + bool _wasSnapshotDataComputingCanceled = false; SnapshotItemToBeCreated? _itemToBeCreated; @@ -225,10 +228,10 @@ class CreateSnapshotCubit extends Cubit { await prepareTx(isArConnectProfile); await signTx(isArConnectProfile); - if (_uploadMethod == UploadMethod.ar) { - snapshotEntity.txId = _preparedTx!.id; - } else { + if (_useTruboUpload) { snapshotEntity.txId = _preparedDataItem!.id; + } else { + snapshotEntity.txId = _preparedTx!.id; } } @@ -242,18 +245,18 @@ class CreateSnapshotCubit extends Cubit { 'Preparing snapshot transaction with ${isArConnectProfile ? 'ArConnect' : 'JSON wallet'}', ); - if (_uploadMethod == UploadMethod.ar) { - _preparedTx = await _arweave.prepareEntityTx( + if (_useTruboUpload) { + _preparedDataItem = await _arweave.prepareEntityDataItem( _snapshotEntity!, wallet, - null, // We'll sign it just after adding the tip skipSignature: true, ); } else { - _preparedDataItem = await _arweave.prepareEntityDataItem( + _preparedTx = await _arweave.prepareEntityTx( _snapshotEntity!, wallet, + null, // We'll sign it just after adding the tip skipSignature: true, ); @@ -293,10 +296,10 @@ class CreateSnapshotCubit extends Cubit { return; } - if (_uploadMethod == UploadMethod.ar) { - await _preparedTx!.sign(wallet); - } else { + if (_useTruboUpload) { await _preparedDataItem!.sign(wallet); + } else { + await _preparedTx!.sign(wallet); } } catch (e) { final isTabFocused = _tabVisibility.isTabFocused(); @@ -541,12 +544,12 @@ class CreateSnapshotCubit extends Cubit { try { emit(UploadingSnapshot()); - if (_uploadMethod == UploadMethod.ar) { - await _arweave.postTx(_preparedTx!); - } else { + if (_useTruboUpload) { await _postTurboDataItem( dataItem: _preparedDataItem!, ); + } else { + await _arweave.postTx(_preparedTx!); } emit(SnapshotUploadSuccess()); From 144aa4eb8fb64b82f21a44752c24e8be977b36b6 Mon Sep 17 00:00:00 2001 From: Mati Date: Wed, 27 Sep 2023 12:30:16 -0300 Subject: [PATCH 12/23] test(create snapshot cubit): fixes brokwn tests PE-4687 --- test/blocs/create_snapshot_cubit_test.dart | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/blocs/create_snapshot_cubit_test.dart b/test/blocs/create_snapshot_cubit_test.dart index 0a5be4f2b5..128b4ddfef 100644 --- a/test/blocs/create_snapshot_cubit_test.dart +++ b/test/blocs/create_snapshot_cubit_test.dart @@ -35,6 +35,18 @@ Future fakePrepareTransaction(invocation) async { return transaction; } +Future fakePrepareDataItem(invocation) async { + final entity = invocation.positionalArguments[0] as SnapshotEntity; + final wallet = invocation.positionalArguments[1] as Wallet; + + final dataItem = await entity.asDataItem(null); + dataItem.setOwner(await wallet.getOwner()); + + await dataItem.sign(wallet); + + return dataItem; +} + class MockAppConfig extends Mock implements AppConfig {} class MockTurboUploadService extends Mock implements TurboUploadService {} @@ -63,6 +75,9 @@ void main() { registerFallbackValue( await getTestTransaction('test/fixtures/signed_v2_tx.json'), ); + registerFallbackValue( + await getTestDataItem('test/fixtures/signed_v2_tx.json'), + ); registerFallbackValue(Future.value()); }); @@ -96,6 +111,12 @@ void main() { ), ).thenAnswer(fakePrepareTransaction); + when(() => arweave.prepareEntityDataItem( + any(), + any(), + skipSignature: any(named: 'skipSignature'), + )).thenAnswer(fakePrepareDataItem); + when(() => arweave.postTx(any())).thenAnswer( (_) async => Future.value(), ); @@ -178,6 +199,16 @@ void main() { .thenAnswer((invocation) => 100); when(() => appConfig.useTurboUpload).thenAnswer((invocation) => true); + when(() => tabVisibility.isTabFocused()) + .thenAnswer((invocation) => true); + + when( + () => turboService.postDataItem( + dataItem: any(named: 'dataItem'), + wallet: any(named: 'wallet'), + ), + ).thenAnswer((invocation) => Future.value(null)); + // mocks PackageInfo PackageInfo.setMockInitialValues( appName: 'appName', From abf45c630258826398907a7d223b405761101161 Mon Sep 17 00:00:00 2001 From: Mati Date: Wed, 27 Sep 2023 13:07:49 -0300 Subject: [PATCH 13/23] feat(create snapshot cubit): corrects typo PE-4687 --- lib/blocs/create_snapshot/create_snapshot_cubit.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 8de333c7e9..d4fb57bd90 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -72,7 +72,7 @@ class CreateSnapshotCubit extends Cubit { bool _sufficientArBalance = false; bool _isFreeThanksToTurbo = false; - bool get _useTruboUpload => + bool get _useTurboUpload => _uploadMethod == UploadMethod.turbo || _isFreeThanksToTurbo; bool _wasSnapshotDataComputingCanceled = false; @@ -228,7 +228,7 @@ class CreateSnapshotCubit extends Cubit { await prepareTx(isArConnectProfile); await signTx(isArConnectProfile); - if (_useTruboUpload) { + if (_useTurboUpload) { snapshotEntity.txId = _preparedDataItem!.id; } else { snapshotEntity.txId = _preparedTx!.id; @@ -245,7 +245,7 @@ class CreateSnapshotCubit extends Cubit { 'Preparing snapshot transaction with ${isArConnectProfile ? 'ArConnect' : 'JSON wallet'}', ); - if (_useTruboUpload) { + if (_useTurboUpload) { _preparedDataItem = await _arweave.prepareEntityDataItem( _snapshotEntity!, wallet, @@ -296,7 +296,7 @@ class CreateSnapshotCubit extends Cubit { return; } - if (_useTruboUpload) { + if (_useTurboUpload) { await _preparedDataItem!.sign(wallet); } else { await _preparedTx!.sign(wallet); @@ -544,7 +544,7 @@ class CreateSnapshotCubit extends Cubit { try { emit(UploadingSnapshot()); - if (_useTruboUpload) { + if (_useTurboUpload) { await _postTurboDataItem( dataItem: _preparedDataItem!, ); From 2e886f9d8397cca9f7eab83bf5b77e245b9cb43f Mon Sep 17 00:00:00 2001 From: Mati Date: Thu, 28 Sep 2023 16:48:19 -0300 Subject: [PATCH 14/23] feat(create snapshot dialog): makes the turbo balance be refreshed on top up sucess PE-4694 --- .../create_snapshot_cubit.dart | 36 +++++++++++++++++++ lib/components/create_snapshot_dialog.dart | 3 +- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index d4fb57bd90..441ec07a95 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -404,6 +404,42 @@ class CreateSnapshotCubit extends Cubit { ); } + Future refreshTurboBalance() async { + final profileState = _profileCubit.state as ProfileLoggedIn; + final wallet = profileState.wallet; + + final turboBalance = + await turboBalanceRetriever.getBalance(wallet).catchError((e) { + logger.e('Error while retrieving turbo balance', e); + return BigInt.zero; + }); + + _turboBalance = turboBalance; + _hasNoTurboBalance = turboBalance == BigInt.zero; + _turboCredits = convertCreditsToLiteralString(turboBalance); + _sufficentCreditsBalance = _costEstimateTurbo.totalCost <= _turboBalance; + _computeIsTurboEnabled(); + _computeIsButtonEnabled(); + + if (state is ConfirmingSnapshotCreation) { + final stateAsConfirming = state as ConfirmingSnapshotCreation; + logger.d('Refreshing turbo balance...'); + logger.d('Turbo balance: $_turboCredits - ($_turboBalance)'); + logger.d('Has no turbo balance: $_hasNoTurboBalance'); + logger + .d('Sufficient balance to pay with turbo: $_sufficentCreditsBalance'); + logger.d('Upload method: $_uploadMethod'); + emit( + stateAsConfirming.copyWith( + turboCredits: _turboCredits, + hasNoTurboBalance: _hasNoTurboBalance, + sufficientBalanceToPayWithTurbo: _sufficentCreditsBalance, + uploadMethod: _uploadMethod, + ), + ); + } + } + Future _computeBalanceEstimate() async { final profileState = _profileCubit.state as ProfileLoggedIn; final wallet = profileState.wallet; diff --git a/lib/components/create_snapshot_dialog.dart b/lib/components/create_snapshot_dialog.dart index 149f0ee59c..f349a4c6ed 100644 --- a/lib/components/create_snapshot_dialog.dart +++ b/lib/components/create_snapshot_dialog.dart @@ -416,8 +416,7 @@ Widget _confirmDialog( state.sufficientBalanceToPayWithTurbo, isFreeThanksToTurbo: false, onTurboTopupSucess: () { - createSnapshotCubit - .setUploadMethod(UploadMethod.turbo); + createSnapshotCubit.refreshTurboBalance(); }, onArSelect: () { createSnapshotCubit.setUploadMethod(UploadMethod.ar); From 853a3dda903b46fb8fe9f95666e2efcc60708c43 Mon Sep 17 00:00:00 2001 From: Mati Date: Thu, 28 Sep 2023 16:49:05 -0300 Subject: [PATCH 15/23] feat(pubspec): updates the ardrive-ui dep PE-4694 --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 0f45e31504..11fb9636fa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -79,8 +79,8 @@ packages: dependency: "direct main" description: path: "." - ref: "v1.10.0" - resolved-ref: "72cd21de7cbd52067924cefac579cdb9d7ef39b7" + ref: PE-4694 + resolved-ref: "031eeac4673b636bbd44f2a91cc03bdcedf9762d" url: "https://github.com/ar-io/ardrive_ui.git" source: git version: "1.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index f98d496d9a..e7aa1ae9d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: ardrive_ui: git: url: https://github.com/ar-io/ardrive_ui.git - ref: v1.10.0 + ref: PE-4694 artemis: ^7.0.0-beta.13 arweave: git: From 3b21e36b731a150e7c0d42ed001a67eb07088567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Batista?= Date: Thu, 28 Sep 2023 16:52:09 -0300 Subject: [PATCH 16/23] chore(create snapshot cubit): cleanup PE-4694 --- lib/blocs/create_snapshot/create_snapshot_cubit.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 441ec07a95..3ea7d9362c 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -423,12 +423,6 @@ class CreateSnapshotCubit extends Cubit { if (state is ConfirmingSnapshotCreation) { final stateAsConfirming = state as ConfirmingSnapshotCreation; - logger.d('Refreshing turbo balance...'); - logger.d('Turbo balance: $_turboCredits - ($_turboBalance)'); - logger.d('Has no turbo balance: $_hasNoTurboBalance'); - logger - .d('Sufficient balance to pay with turbo: $_sufficentCreditsBalance'); - logger.d('Upload method: $_uploadMethod'); emit( stateAsConfirming.copyWith( turboCredits: _turboCredits, From b5ba8fc42b4601304799cd2f407a02f12c541772 Mon Sep 17 00:00:00 2001 From: Mati Date: Mon, 2 Oct 2023 13:47:15 -0300 Subject: [PATCH 17/23] feat(create snapshot cubit): adds a delay to ensure we'll read the corect turbo balance PE-4694 --- lib/blocs/create_snapshot/create_snapshot_cubit.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 3ea7d9362c..e37de025f0 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -408,6 +408,9 @@ class CreateSnapshotCubit extends Cubit { final profileState = _profileCubit.state as ProfileLoggedIn; final wallet = profileState.wallet; + /// necessary to wait for backend update the balance + await Future.delayed(const Duration(seconds: 2)); + final turboBalance = await turboBalanceRetriever.getBalance(wallet).catchError((e) { logger.e('Error while retrieving turbo balance', e); From 6d84457a57f65c6fcb50559a2228fc5514e3f8ae Mon Sep 17 00:00:00 2001 From: Mati Date: Mon, 2 Oct 2023 13:53:03 -0300 Subject: [PATCH 18/23] feat(dev tools): adds optoins for disabling free uploads, setting a fake amount of turbo balance, and dry run for the topup PE-4694 --- .../create_snapshot_cubit.dart | 50 +++-- lib/components/create_snapshot_dialog.dart | 2 +- lib/dev_tools/app_dev_tools.dart | 191 +++++++++++++----- lib/services/config/app_config.dart | 47 +++++ lib/services/config/config_service.dart | 7 +- .../payment_review/payment_review_bloc.dart | 4 +- .../payment_review/payment_review_event.dart | 2 + lib/turbo/topup/views/topup_modal.dart | 32 ++- lib/turbo/topup/views/topup_review_view.dart | 12 +- lib/turbo/turbo.dart | 11 +- 10 files changed, 270 insertions(+), 88 deletions(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index e37de025f0..63534966ff 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -11,9 +11,7 @@ import 'package:ardrive/entities/entities.dart'; import 'package:ardrive/entities/snapshot_entity.dart'; import 'package:ardrive/entities/string_types.dart'; import 'package:ardrive/models/daos/daos.dart'; -import 'package:ardrive/services/arweave/arweave.dart'; -import 'package:ardrive/services/config/app_config.dart'; -import 'package:ardrive/services/pst/pst.dart'; +import 'package:ardrive/services/services.dart'; import 'package:ardrive/turbo/services/payment_service.dart'; import 'package:ardrive/turbo/services/upload_service.dart'; import 'package:ardrive/turbo/turbo.dart'; @@ -42,7 +40,7 @@ class CreateSnapshotCubit extends Cubit { final TabVisibilitySingleton _tabVisibility; final TurboBalanceRetriever turboBalanceRetriever; final ArDriveAuth auth; - final AppConfig appConfig; + final ConfigService configService; final TurboUploadService turboService; @visibleForTesting bool throwOnDataComputingForTesting; @@ -71,11 +69,12 @@ class CreateSnapshotCubit extends Cubit { bool _sufficentCreditsBalance = false; bool _sufficientArBalance = false; bool _isFreeThanksToTurbo = false; + bool _wasSnapshotDataComputingCanceled = false; bool get _useTurboUpload => _uploadMethod == UploadMethod.turbo || _isFreeThanksToTurbo; - bool _wasSnapshotDataComputingCanceled = false; + AppConfig get appConfig => configService.config; SnapshotItemToBeCreated? _itemToBeCreated; SnapshotEntity? _snapshotEntity; @@ -89,7 +88,7 @@ class CreateSnapshotCubit extends Cubit { required TabVisibilitySingleton tabVisibility, required this.turboBalanceRetriever, required this.auth, - required this.appConfig, + required this.configService, required this.turboService, this.throwOnDataComputingForTesting = false, this.throwOnSignTxForTesting = false, @@ -408,14 +407,18 @@ class CreateSnapshotCubit extends Cubit { final profileState = _profileCubit.state as ProfileLoggedIn; final wallet = profileState.wallet; + final BigInt? fakeTurboCredits = appConfig.fakeTurboCredits; + /// necessary to wait for backend update the balance await Future.delayed(const Duration(seconds: 2)); - final turboBalance = + final BigInt turboBalance = fakeTurboCredits ?? await turboBalanceRetriever.getBalance(wallet).catchError((e) { - logger.e('Error while retrieving turbo balance', e); - return BigInt.zero; - }); + logger.e('Error while retrieving turbo balance', e); + return BigInt.zero; + }); + + logger.d('Balance after topping up: $turboBalance'); _turboBalance = turboBalance; _hasNoTurboBalance = turboBalance == BigInt.zero; @@ -426,6 +429,14 @@ class CreateSnapshotCubit extends Cubit { if (state is ConfirmingSnapshotCreation) { final stateAsConfirming = state as ConfirmingSnapshotCreation; + + logger.d('Refreshing turbo balance'); + logger.d('Turbo balance: $_turboCredits'); + logger.d('Has no turbo balance: $_hasNoTurboBalance'); + logger + .d('Sufficient balance to pay with turbo: $_sufficentCreditsBalance'); + logger.d('Upload method: $_uploadMethod'); + emit( stateAsConfirming.copyWith( turboCredits: _turboCredits, @@ -438,14 +449,18 @@ class CreateSnapshotCubit extends Cubit { } Future _computeBalanceEstimate() async { - final profileState = _profileCubit.state as ProfileLoggedIn; - final wallet = profileState.wallet; + final ProfileLoggedIn profileState = _profileCubit.state as ProfileLoggedIn; + final Wallet wallet = profileState.wallet; - final turboBalance = + final BigInt? fakeTurboCredits = appConfig.fakeTurboCredits; + + final BigInt turboBalance = fakeTurboCredits ?? await turboBalanceRetriever.getBalance(wallet).catchError((e) { - logger.e('Error while retrieving turbo balance', e); - return BigInt.zero; - }); + logger.e('Error while retrieving turbo balance', e); + return BigInt.zero; + }); + + logger.d('Balance before topping up: $turboBalance'); _turboBalance = turboBalance; _hasNoTurboBalance = turboBalance == BigInt.zero; @@ -472,9 +487,10 @@ class CreateSnapshotCubit extends Cubit { void _computeIsFreeThanksToTurbo() { final allowedDataItemSizeForTurbo = appConfig.allowedDataItemSizeForTurbo; + final forceNoFreeThanksToTurbo = appConfig.forceNoFreeThanksToTurbo; final isFreeThanksToTurbo = _snapshotEntity!.data!.length <= allowedDataItemSizeForTurbo; - _isFreeThanksToTurbo = isFreeThanksToTurbo; + _isFreeThanksToTurbo = isFreeThanksToTurbo && !forceNoFreeThanksToTurbo; } Future _emitConfirming({required int dataSize}) async { diff --git a/lib/components/create_snapshot_dialog.dart b/lib/components/create_snapshot_dialog.dart index f349a4c6ed..9cfa9d8ba6 100644 --- a/lib/components/create_snapshot_dialog.dart +++ b/lib/components/create_snapshot_dialog.dart @@ -43,7 +43,7 @@ Future promptToCreateSnapshot( turboBalanceRetriever: TurboBalanceRetriever( paymentService: context.read(), ), - appConfig: context.read().config, + configService: context.read(), turboService: context.read(), ), child: CreateSnapshotDialog( diff --git a/lib/dev_tools/app_dev_tools.dart b/lib/dev_tools/app_dev_tools.dart index fa17f022aa..c84a381a07 100644 --- a/lib/dev_tools/app_dev_tools.dart +++ b/lib/dev_tools/app_dev_tools.dart @@ -98,161 +98,165 @@ class AppConfigWindowManagerState extends State { @override Widget build(BuildContext context) { - final settings = context.read().config; - final configService = context.read(); + final ConfigService configService = context.read(); + final AppConfig config = configService.config; - ArDriveDevToolOption defaultArweaveGatewayUrlOption = ArDriveDevToolOption( + final ArDriveDevToolOption defaultArweaveGatewayUrlOption = + ArDriveDevToolOption( name: 'defaultArweaveGatewayUrl', - value: settings.defaultArweaveGatewayUrl, + value: config.defaultArweaveGatewayUrl, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(defaultArweaveGatewayUrl: value), + config.copyWith(defaultArweaveGatewayUrl: value), ); }); }, type: ArDriveDevToolOptionType.text, ); - ArDriveDevToolOption useTurboOption = ArDriveDevToolOption( + final ArDriveDevToolOption useTurboOption = ArDriveDevToolOption( name: 'useTurboUpload', - value: settings.useTurboUpload, + value: config.useTurboUpload, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(useTurboUpload: value), + config.copyWith(useTurboUpload: value), ); }); }, type: ArDriveDevToolOptionType.bool, ); - ArDriveDevToolOption useTurboPaymentOption = ArDriveDevToolOption( + final ArDriveDevToolOption useTurboPaymentOption = ArDriveDevToolOption( name: 'useTurboPayment', - value: settings.useTurboPayment, + value: config.useTurboPayment, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(useTurboPayment: value), + config.copyWith(useTurboPayment: value), ); }); }, type: ArDriveDevToolOptionType.bool, ); - ArDriveDevToolOption defaultTurboUrlOption = ArDriveDevToolOption( + final ArDriveDevToolOption defaultTurboUrlOption = ArDriveDevToolOption( name: 'defaultTurboUrl', - value: settings.defaultTurboUploadUrl, + value: config.defaultTurboUploadUrl, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(defaultTurboUploadUrl: value), + config.copyWith(defaultTurboUploadUrl: value), ); }); }, type: ArDriveDevToolOptionType.text, ); - ArDriveDevToolOption defaultTurboPaymentUrlOption = ArDriveDevToolOption( + final ArDriveDevToolOption defaultTurboPaymentUrlOption = + ArDriveDevToolOption( name: 'defaultTurboUrl', - value: settings.defaultTurboPaymentUrl, + value: config.defaultTurboPaymentUrl, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(defaultTurboPaymentUrl: value), + config.copyWith(defaultTurboPaymentUrl: value), ); }); }, type: ArDriveDevToolOptionType.text, ); - ArDriveDevToolOption stripePublishableKey = ArDriveDevToolOption( + final ArDriveDevToolOption stripePublishableKey = ArDriveDevToolOption( name: 'stripePublishableKey', - value: settings.stripePublishableKey, + value: config.stripePublishableKey, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(stripePublishableKey: value), + config.copyWith(stripePublishableKey: value), ); }); }, type: ArDriveDevToolOptionType.text, ); - ArDriveDevToolOption allowedDataItemSizeForTurboOption = + final ArDriveDevToolOption allowedDataItemSizeForTurboOption = ArDriveDevToolOption( name: 'allowedDataItemSizeForTurbo', - value: settings.allowedDataItemSizeForTurbo, + value: config.allowedDataItemSizeForTurbo, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(allowedDataItemSizeForTurbo: value), + config.copyWith(allowedDataItemSizeForTurbo: value), ); }); }, type: ArDriveDevToolOptionType.number, ); - ArDriveDevToolOption enableQuickSyncAuthoringOption = ArDriveDevToolOption( + final ArDriveDevToolOption enableQuickSyncAuthoringOption = + ArDriveDevToolOption( name: 'enableQuickSyncAuthoring', - value: settings.enableQuickSyncAuthoring, + value: config.enableQuickSyncAuthoring, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(enableQuickSyncAuthoring: value), + config.copyWith(enableQuickSyncAuthoring: value), ); }); }, type: ArDriveDevToolOptionType.bool, ); - ArDriveDevToolOption enableMultipleFileDownloadOption = + final ArDriveDevToolOption enableMultipleFileDownloadOption = ArDriveDevToolOption( name: 'enableMultipleFileDownload', - value: settings.enableMultipleFileDownload, + value: config.enableMultipleFileDownload, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(enableMultipleFileDownload: value), + config.copyWith(enableMultipleFileDownload: value), ); }); }, type: ArDriveDevToolOptionType.bool, ); - ArDriveDevToolOption enableVideoPreviewOption = ArDriveDevToolOption( + final ArDriveDevToolOption enableVideoPreviewOption = ArDriveDevToolOption( name: 'enableVideoPreview', - value: settings.enableVideoPreview, + value: config.enableVideoPreview, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(enableVideoPreview: value), + config.copyWith(enableVideoPreview: value), ); }); }, type: ArDriveDevToolOptionType.bool, ); - ArDriveDevToolOption enableAudioPreviewOption = ArDriveDevToolOption( + final ArDriveDevToolOption enableAudioPreviewOption = ArDriveDevToolOption( name: 'enableAudioPreview', - value: settings.enableAudioPreview, + value: config.enableAudioPreview, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(enableAudioPreview: value), + config.copyWith(enableAudioPreview: value), ); }); }, type: ArDriveDevToolOptionType.bool, ); - ArDriveDevToolOption autoSyncIntervalInSecondsOption = ArDriveDevToolOption( + final ArDriveDevToolOption autoSyncIntervalInSecondsOption = + ArDriveDevToolOption( name: 'autoSyncIntervalInSeconds', - value: settings.autoSyncIntervalInSeconds, + value: config.autoSyncIntervalInSeconds, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(autoSyncIntervalInSeconds: value), + config.copyWith(autoSyncIntervalInSeconds: value), ); }); }, @@ -260,7 +264,7 @@ class AppConfigWindowManagerState extends State { ); // reload option - ArDriveDevToolOption reloadOption = ArDriveDevToolOption( + final ArDriveDevToolOption reloadOption = ArDriveDevToolOption( name: 'Reload', value: '', onChange: (value) { @@ -269,7 +273,7 @@ class AppConfigWindowManagerState extends State { type: ArDriveDevToolOptionType.button, ); - ArDriveDevToolOption resetOptions = ArDriveDevToolOption( + final ArDriveDevToolOption resetOptions = ArDriveDevToolOption( name: 'Reset options', value: '', onChange: (value) async { @@ -284,26 +288,27 @@ class AppConfigWindowManagerState extends State { type: ArDriveDevToolOptionType.buttonTertiary, ); - ArDriveDevToolOption enableSyncFromSnapshotOption = ArDriveDevToolOption( + final ArDriveDevToolOption enableSyncFromSnapshotOption = + ArDriveDevToolOption( name: 'enableSyncFromSnapshot', - value: settings.enableSyncFromSnapshot, + value: config.enableSyncFromSnapshot, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(enableSyncFromSnapshot: value), + config.copyWith(enableSyncFromSnapshot: value), ); }); }, type: ArDriveDevToolOptionType.bool, ); - ArDriveDevToolOption enableSeedPhreaseLogin = ArDriveDevToolOption( + final ArDriveDevToolOption enableSeedPhreaseLogin = ArDriveDevToolOption( name: 'enableSeedPhreaseLogin', - value: settings.enableSeedPhraseLogin, + value: config.enableSeedPhraseLogin, onChange: (value) { setState(() { configService.updateAppConfig( - settings.copyWith(enableSeedPhraseLogin: value), + config.copyWith(enableSeedPhraseLogin: value), ); }); }, @@ -311,7 +316,7 @@ class AppConfigWindowManagerState extends State { ); // reload option - ArDriveDevToolOption turboSetDefaultData = ArDriveDevToolOption( + final ArDriveDevToolOption turboSetDefaultData = ArDriveDevToolOption( name: 'setDefaultDataOnPaymentForm', value: '', onChange: (value) {}, @@ -327,7 +332,50 @@ class AppConfigWindowManagerState extends State { type: ArDriveDevToolOptionType.button, ); - List options = [ + final ArDriveDevToolOption forceNoFreeThanksToTurbo = ArDriveDevToolOption( + name: 'forceNoFreeThanksToTurbo', + value: config.forceNoFreeThanksToTurbo, + onChange: (value) { + setState(() { + configService.updateAppConfig( + config.copyWith(forceNoFreeThanksToTurbo: value), + ); + }); + }, + type: ArDriveDevToolOptionType.bool, + ); + + final ArDriveDevToolOption fakeTurboCredits = ArDriveDevToolOption( + name: 'fakeTurboCredits', + value: config.fakeTurboCredits, + onChange: (value) { + late AppConfig newConfig; + if (value == null) { + newConfig = config.copyWith(unsetFakeTurboCredits: true); + } else { + newConfig = config.copyWith(fakeTurboCredits: value); + } + setState(() { + configService.updateAppConfig(newConfig); + }); + }, + type: ArDriveDevToolOptionType.turboCredits, + ); + + final ArDriveDevToolOption topUpDryRun = ArDriveDevToolOption( + name: 'topUpDryRun', + value: config.topUpDryRun, + onChange: (value) { + setState(() { + configService.updateAppConfig( + config.copyWith(topUpDryRun: value), + ); + }); + }, + type: ArDriveDevToolOptionType.bool, + ); + + final List options = [ useTurboOption, useTurboPaymentOption, defaultTurboPaymentUrlOption, @@ -343,6 +391,9 @@ class AppConfigWindowManagerState extends State { defaultTurboUrlOption, autoSyncIntervalInSecondsOption, turboSetDefaultData, + forceNoFreeThanksToTurbo, + fakeTurboCredits, + topUpDryRun, reloadOption, resetOptions, ]; @@ -465,12 +516,39 @@ class AppConfigWindowManagerState extends State { option.onInteraction?.call(); }, ); + case ArDriveDevToolOptionType.buttonTertiary: return ArDriveButton( style: ArDriveButtonStyle.tertiary, text: option.name, onPressed: () => option.onChange(option.value), ); + + case ArDriveDevToolOptionType.turboCredits: + final optionAsBigInt = option as ArDriveDevToolOption; + return ArDriveTextField( + label: optionAsBigInt.name, + initialValue: optionAsBigInt.value != null + ? (optionAsBigInt.value! / BigInt.from(1000000000000)).toString() + : '', + onFieldSubmitted: (value) { + final doubleVaue = double.tryParse(value); + if (doubleVaue == null) { + optionAsBigInt.onChange(null); + showOptionSavedMessage(); + return; + } + + final winstonCredits = BigInt.from( + (doubleVaue * 1000000000000).floor(), + ); + optionAsBigInt.onChange(winstonCredits); + showOptionSavedMessage(); + }, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,12}')), + ], + ); } } @@ -599,14 +677,21 @@ class DraggableWindow extends HookWidget { } } -enum ArDriveDevToolOptionType { text, bool, number, button, buttonTertiary } +enum ArDriveDevToolOptionType { + text, + bool, + number, + button, + buttonTertiary, + turboCredits, +} -typedef OnChange = void Function(dynamic value); +typedef OnChange = void Function(T value); -class ArDriveDevToolOption { +class ArDriveDevToolOption { final String name; - dynamic value; - final OnChange onChange; + T value; + final OnChange onChange; final ArDriveDevToolOptionType type; final Function? onInteraction; diff --git a/lib/services/config/app_config.dart b/lib/services/config/app_config.dart index 3b3725c5a0..8d2600754c 100644 --- a/lib/services/config/app_config.dart +++ b/lib/services/config/app_config.dart @@ -1,3 +1,4 @@ +import 'package:ardrive/utils/logger/logger.dart'; import 'package:json_annotation/json_annotation.dart'; part 'app_config.g.dart'; @@ -18,6 +19,9 @@ class AppConfig { final bool enableSyncFromSnapshot; final bool enableSeedPhraseLogin; final String stripePublishableKey; + final bool forceNoFreeThanksToTurbo; + final BigInt? fakeTurboCredits; + final bool topUpDryRun; AppConfig({ this.defaultArweaveGatewayUrl, @@ -34,6 +38,9 @@ class AppConfig { this.enableSyncFromSnapshot = true, this.enableSeedPhraseLogin = true, required this.stripePublishableKey, + this.forceNoFreeThanksToTurbo = false, + this.fakeTurboCredits, + this.topUpDryRun = false, }); AppConfig copyWith({ @@ -51,7 +58,15 @@ class AppConfig { bool? enableSyncFromSnapshot, bool? enableSeedPhraseLogin, String? stripePublishableKey, + bool? forceNoFreeThanksToTurbo, + BigInt? fakeTurboCredits, + bool? topUpDryRun, + bool? unsetFakeTurboCredits, }) { + final theFakeTurboCredits = unsetFakeTurboCredits == true + ? null + : fakeTurboCredits ?? this.fakeTurboCredits; + return AppConfig( defaultArweaveGatewayUrl: defaultArweaveGatewayUrl ?? this.defaultArweaveGatewayUrl, @@ -76,9 +91,41 @@ class AppConfig { enableSeedPhraseLogin: enableSeedPhraseLogin ?? this.enableSeedPhraseLogin, stripePublishableKey: stripePublishableKey ?? this.stripePublishableKey, + forceNoFreeThanksToTurbo: + forceNoFreeThanksToTurbo ?? this.forceNoFreeThanksToTurbo, + fakeTurboCredits: theFakeTurboCredits, + topUpDryRun: topUpDryRun ?? this.topUpDryRun, ); } + String diff(AppConfig other) { + // Compares this and the given AppConfig and returns a csv string + /// representing the differences. + + final thisJson = toJson(); + final otherJson = other.toJson(); + + final keysOfThis = thisJson.keys; + final keysOfOther = otherJson.keys; + final Set allKeys = {...keysOfThis, ...keysOfOther}; + + logger.d('All keys: $allKeys'); + logger.d('This: $thisJson'); + logger.d('Other: $otherJson'); + + final List diffs = []; + for (final key in allKeys) { + final valueOfThis = thisJson[key]; + final valueOfOther = otherJson[key]; + + if (valueOfThis != valueOfOther) { + diffs.add('$key: $valueOfThis -> $valueOfOther'); + } + } + + return diffs.join(', '); + } + @override String toString() => 'AppConfig(${toJson()})'; diff --git a/lib/services/config/config_service.dart b/lib/services/config/config_service.dart index b7c12fc67b..2d7e405ab7 100644 --- a/lib/services/config/config_service.dart +++ b/lib/services/config/config_service.dart @@ -55,9 +55,10 @@ class ConfigService { } } - void updateAppConfig(AppConfig config) { - _configFetcher.saveConfigOnDevToolsPrefs(config); - _config = config; + void updateAppConfig(AppConfig newConfig) { + logger.d('App config updated: ${config.diff(newConfig)}'); + _configFetcher.saveConfigOnDevToolsPrefs(newConfig); + _config = newConfig; } Future resetDevToolsPrefs() async { diff --git a/lib/turbo/topup/blocs/payment_review/payment_review_bloc.dart b/lib/turbo/topup/blocs/payment_review/payment_review_bloc.dart index 8a55fb8f6c..fad9f3a497 100644 --- a/lib/turbo/topup/blocs/payment_review/payment_review_bloc.dart +++ b/lib/turbo/topup/blocs/payment_review/payment_review_bloc.dart @@ -47,7 +47,9 @@ class PaymentReviewBloc extends Bloc { userAcceptedToReceiveEmails: event.userAcceptedToReceiveEmails, ); - final paymentStatus = await turbo.confirmPayment(); + final paymentStatus = await turbo.confirmPayment( + dryRun: event.dryRun, + ); if (paymentStatus == PaymentStatus.success) { _emitPaymentSuccess(emit); diff --git a/lib/turbo/topup/blocs/payment_review/payment_review_event.dart b/lib/turbo/topup/blocs/payment_review/payment_review_event.dart index e5abbd7285..0a5c0a1518 100644 --- a/lib/turbo/topup/blocs/payment_review/payment_review_event.dart +++ b/lib/turbo/topup/blocs/payment_review/payment_review_event.dart @@ -10,10 +10,12 @@ abstract class PaymentReviewEvent extends Equatable { class PaymentReviewFinishPayment extends PaymentReviewEvent { final String? email; final bool userAcceptedToReceiveEmails; + final bool dryRun; const PaymentReviewFinishPayment({ this.email, this.userAcceptedToReceiveEmails = false, + required this.dryRun, }); @override diff --git a/lib/turbo/topup/views/topup_modal.dart b/lib/turbo/topup/views/topup_modal.dart index 10af47e78d..bcfce64ed0 100644 --- a/lib/turbo/topup/views/topup_modal.dart +++ b/lib/turbo/topup/views/topup_modal.dart @@ -2,7 +2,7 @@ import 'package:animations/animations.dart'; import 'package:ardrive/authentication/ardrive_auth.dart'; import 'package:ardrive/components/top_up_dialog.dart'; import 'package:ardrive/core/activity_tracker.dart'; -import 'package:ardrive/services/config/config_service.dart'; +import 'package:ardrive/services/services.dart'; import 'package:ardrive/turbo/services/payment_service.dart'; import 'package:ardrive/turbo/topup/blocs/payment_form/payment_form_bloc.dart'; import 'package:ardrive/turbo/topup/blocs/payment_review/payment_review_bloc.dart'; @@ -22,6 +22,7 @@ import 'package:flutter_stripe/flutter_stripe.dart'; void showTurboTopupModal(BuildContext context, {Function()? onSuccess}) { final activityTracker = context.read(); final sessionManager = TurboSessionManager(); + final appConfig = context.read().config; final costCalculator = TurboCostCalculator( paymentService: context.read(), @@ -55,7 +56,7 @@ void showTurboTopupModal(BuildContext context, {Function()? onSuccess}) { supportedCountriesRetriever: turboSupportedCountriesRetriever, ); - initializeStripe(context.read().config); + initializeStripe(appConfig); activityTracker.setToppingUp(true); @@ -75,7 +76,10 @@ void showTurboTopupModal(BuildContext context, {Function()? onSuccess}) { )..add(LoadInitialData()), ), ], - child: TurboModal(parentContext: modalContext), + child: TurboModal( + parentContext: modalContext, + appConfig: appConfig, + ), ), barrierDismissible: false, barrierColor: @@ -83,8 +87,6 @@ void showTurboTopupModal(BuildContext context, {Function()? onSuccess}) { ).then((value) { logger.d('Turbo modal closed with value: ${turbo.paymentStatus}'); - activityTracker.setToppingUp(false); - if (turbo.paymentStatus == PaymentStatus.success) { logger.d('Turbo payment success'); @@ -92,14 +94,21 @@ void showTurboTopupModal(BuildContext context, {Function()? onSuccess}) { } turbo.dispose(); + }).whenComplete(() { + activityTracker.setToppingUp(false); }); } class TurboModal extends StatefulWidget { - const TurboModal({super.key, required this.parentContext}); - + final AppConfig _appConfig; final BuildContext parentContext; + const TurboModal({ + super.key, + required this.parentContext, + required AppConfig appConfig, + }) : _appConfig = appConfig; + @override State createState() => _TurboModalState(); } @@ -205,9 +214,12 @@ class _TurboModalState extends State with TickerProviderStateMixin { state.priceEstimate, )..add(PaymentReviewLoadPaymentModel()), child: Container( - color: - ArDriveTheme.of(context).themeData.colors.themeBgCanvas, - child: const TurboReviewView()), + color: + ArDriveTheme.of(context).themeData.colors.themeBgCanvas, + child: TurboReviewView( + dryRun: widget._appConfig.topUpDryRun, + ), + ), ), ], ); diff --git a/lib/turbo/topup/views/topup_review_view.dart b/lib/turbo/topup/views/topup_review_view.dart index ab1d994bd4..1b580619a5 100644 --- a/lib/turbo/topup/views/topup_review_view.dart +++ b/lib/turbo/topup/views/topup_review_view.dart @@ -16,7 +16,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:responsive_builder/responsive_builder.dart'; class TurboReviewView extends StatefulWidget { - const TurboReviewView({super.key}); + final bool dryRun; + const TurboReviewView({ + super.key, + required this.dryRun, + }); @override State createState() => _TurboReviewViewState(); @@ -581,6 +585,8 @@ class _TurboReviewViewState extends State { BlocBuilder( builder: (context, state) { return ScreenTypeLayout.builder( + // FIXME: the desctop section is never gonna be rendered + /// because its wrapped in another layout builder for mobile. desktop: (context) => ArDriveButton( maxHeight: 44, maxWidth: 143, @@ -610,6 +616,7 @@ class _TurboReviewViewState extends State { PaymentReviewFinishPayment( email: _emailController.text, userAcceptedToReceiveEmails: _emailChecked, + dryRun: widget.dryRun, ), ); }, @@ -643,6 +650,7 @@ class _TurboReviewViewState extends State { PaymentReviewFinishPayment( email: _emailController.text, userAcceptedToReceiveEmails: _emailChecked, + dryRun: widget.dryRun, ), ); }, @@ -729,6 +737,7 @@ class _TurboReviewViewState extends State { PaymentReviewFinishPayment( email: _emailController.text, userAcceptedToReceiveEmails: _emailChecked, + dryRun: widget.dryRun, ), ); }, @@ -762,6 +771,7 @@ class _TurboReviewViewState extends State { PaymentReviewFinishPayment( email: _emailController.text, userAcceptedToReceiveEmails: _emailChecked, + dryRun: widget.dryRun, ), ); }, diff --git a/lib/turbo/turbo.dart b/lib/turbo/turbo.dart index 9796b914b6..dc0e49bc2a 100644 --- a/lib/turbo/turbo.dart +++ b/lib/turbo/turbo.dart @@ -182,14 +182,21 @@ class Turbo extends Disposable { return _currentPaymentIntent!; } - Future confirmPayment() async { + Future confirmPayment({ + bool dryRun = false, + }) async { if (_currentPaymentIntent == null) { throw Exception( 'Current payment intent is null. You should create it before calling this method.'); } - logger.d('Confirming payment with payment provider'); + if (dryRun) { + logger.d('Confirming payment with dry run'); + _paymentStatus = PaymentStatus.success; + return _paymentStatus!; + } + logger.d('Confirming payment with payment provider'); _paymentStatus = await _paymentProvider.confirmPayment( paymentUserInformation: paymentUserInformation, paymentModel: _currentPaymentIntent!, From 4d1bf6cb6790324365c1a01dbbcb7da6d2ba4cef Mon Sep 17 00:00:00 2001 From: Mati Date: Mon, 2 Oct 2023 13:56:43 -0300 Subject: [PATCH 19/23] feat(create snapshot cubit): update the state of the button after topup success PE-4694 --- lib/blocs/create_snapshot/create_snapshot_cubit.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 63534966ff..945aa618c8 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -443,6 +443,7 @@ class CreateSnapshotCubit extends Cubit { hasNoTurboBalance: _hasNoTurboBalance, sufficientBalanceToPayWithTurbo: _sufficentCreditsBalance, uploadMethod: _uploadMethod, + isButtonToUploadEnabled: _isButtonToUploadEnabled, ), ); } From cf192533e8d82d5541cbff4eb7e56a90dfa79b65 Mon Sep 17 00:00:00 2001 From: Mati Date: Mon, 2 Oct 2023 15:25:12 -0300 Subject: [PATCH 20/23] test(create snapshot cubit): updates broken tests PE-4694 --- lib/services/config/config_service.dart | 2 +- .../payment_review/payment_review_event.dart | 2 +- test/blocs/create_snapshot_cubit_test.dart | 25 ++++++++++++------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/services/config/config_service.dart b/lib/services/config/config_service.dart index 2d7e405ab7..fd312396cf 100644 --- a/lib/services/config/config_service.dart +++ b/lib/services/config/config_service.dart @@ -56,7 +56,7 @@ class ConfigService { } void updateAppConfig(AppConfig newConfig) { - logger.d('App config updated: ${config.diff(newConfig)}'); + // logger.d('App config updated: ${config.diff(newConfig)}'); _configFetcher.saveConfigOnDevToolsPrefs(newConfig); _config = newConfig; } diff --git a/lib/turbo/topup/blocs/payment_review/payment_review_event.dart b/lib/turbo/topup/blocs/payment_review/payment_review_event.dart index 0a5c0a1518..97ac2d813c 100644 --- a/lib/turbo/topup/blocs/payment_review/payment_review_event.dart +++ b/lib/turbo/topup/blocs/payment_review/payment_review_event.dart @@ -15,7 +15,7 @@ class PaymentReviewFinishPayment extends PaymentReviewEvent { const PaymentReviewFinishPayment({ this.email, this.userAcceptedToReceiveEmails = false, - required this.dryRun, + this.dryRun = false, }); @override diff --git a/test/blocs/create_snapshot_cubit_test.dart b/test/blocs/create_snapshot_cubit_test.dart index 128b4ddfef..332c8f04a8 100644 --- a/test/blocs/create_snapshot_cubit_test.dart +++ b/test/blocs/create_snapshot_cubit_test.dart @@ -63,6 +63,7 @@ void main() { final pst = MockPstService(); final tabVisibility = MockTabVisibilitySingleton(); final testWallet = getTestWallet(); + final configService = MockConfigService(); final appConfig = MockAppConfig(); final auth = MockArDriveAuth(); final paymentService = MockPaymentService(); @@ -198,6 +199,12 @@ void main() { when(() => appConfig.allowedDataItemSizeForTurbo) .thenAnswer((invocation) => 100); when(() => appConfig.useTurboUpload).thenAnswer((invocation) => true); + when(() => appConfig.forceNoFreeThanksToTurbo) + .thenAnswer((invocation) => false); + when(() => appConfig.fakeTurboCredits).thenAnswer((invocation) => null); + when(() => appConfig.topUpDryRun).thenAnswer((invocation) => false); + + when(() => configService.config).thenAnswer((invocation) => appConfig); when(() => tabVisibility.isTabFocused()) .thenAnswer((invocation) => true); @@ -228,7 +235,7 @@ void main() { tabVisibility: tabVisibility, pst: pst, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -245,7 +252,7 @@ void main() { tabVisibility: tabVisibility, pst: pst, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -272,7 +279,7 @@ void main() { tabVisibility: tabVisibility, pst: pst, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -305,7 +312,7 @@ void main() { pst: pst, throwOnDataComputingForTesting: true, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -332,7 +339,7 @@ void main() { tabVisibility: tabVisibility, pst: pst, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -360,7 +367,7 @@ void main() { pst: pst, throwOnDataComputingForTesting: true, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -387,7 +394,7 @@ void main() { tabVisibility: tabVisibility, pst: pst, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -471,7 +478,7 @@ void main() { tabVisibility: tabVisibility, pst: pst, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, @@ -501,7 +508,7 @@ void main() { pst: pst, throwOnSignTxForTesting: true, auth: auth, - appConfig: appConfig, + configService: configService, paymentService: paymentService, turboBalanceRetriever: turboBalanceRetriever, turboService: turboService, From dfb546df2a0eb7abcf2fd58449bc2bbf9968a4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Batista?= Date: Tue, 3 Oct 2023 14:00:11 -0300 Subject: [PATCH 21/23] chore(topup review view): corrects typo in a comment PE-4694 Co-authored-by: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> --- lib/turbo/topup/views/topup_review_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/turbo/topup/views/topup_review_view.dart b/lib/turbo/topup/views/topup_review_view.dart index 1b580619a5..a537c9a14f 100644 --- a/lib/turbo/topup/views/topup_review_view.dart +++ b/lib/turbo/topup/views/topup_review_view.dart @@ -585,7 +585,7 @@ class _TurboReviewViewState extends State { BlocBuilder( builder: (context, state) { return ScreenTypeLayout.builder( - // FIXME: the desctop section is never gonna be rendered + // FIXME: the desktop section is never gonna be rendered /// because its wrapped in another layout builder for mobile. desktop: (context) => ArDriveButton( maxHeight: 44, From 26ef3cf0d62a2bb543f8e62263e9e37208a3fc10 Mon Sep 17 00:00:00 2001 From: Mati Date: Wed, 4 Oct 2023 13:07:20 -0300 Subject: [PATCH 22/23] chore(create snapshot cubit): removes unnecessary non-null assertion PE-3173 --- lib/blocs/create_snapshot/create_snapshot_cubit.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/blocs/create_snapshot/create_snapshot_cubit.dart b/lib/blocs/create_snapshot/create_snapshot_cubit.dart index 945aa618c8..6c1c733f9a 100644 --- a/lib/blocs/create_snapshot/create_snapshot_cubit.dart +++ b/lib/blocs/create_snapshot/create_snapshot_cubit.dart @@ -466,7 +466,7 @@ class CreateSnapshotCubit extends Cubit { _turboBalance = turboBalance; _hasNoTurboBalance = turboBalance == BigInt.zero; _turboCredits = convertCreditsToLiteralString(turboBalance); - _arBalance = convertCreditsToLiteralString(auth.currentUser!.walletBalance); + _arBalance = convertCreditsToLiteralString(auth.currentUser.walletBalance); } void _computeIsTurboEnabled() async { From ef0493c94e1b1e64cd4d5470eda95e029f1d2174 Mon Sep 17 00:00:00 2001 From: Mati Date: Wed, 4 Oct 2023 15:38:02 -0300 Subject: [PATCH 23/23] feat(pubspec): version bump of ardrive-ui PE-3173 --- pubspec.lock | 6 +++--- pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 512df549b2..9d98abad6c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -79,11 +79,11 @@ packages: dependency: "direct main" description: path: "." - ref: PE-4694 - resolved-ref: a775a227ac94b21c1ef05921a43abfa1ebddf3a5 + ref: "v1.12.0" + resolved-ref: e8af5815f7e1c27fa72544a379971d4630599805 url: "https://github.com/ar-io/ardrive_ui.git" source: git - version: "1.11.0" + version: "1.12.0" args: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 123c25e7c1..3bea42c1b2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: ardrive_ui: git: url: https://github.com/ar-io/ardrive_ui.git - ref: PE-4694 + ref: v1.12.0 artemis: ^7.0.0-beta.13 arweave: git: