diff --git a/assets b/assets index 0dc5aec06..451a4648c 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0dc5aec06f409bfa5b3159fc03981be53571143f +Subproject commit 451a4648c9753eb1971e32d0760736e0dab8f99b diff --git a/lib/screen/interactive_postcard/postcard_detail_bloc.dart b/lib/screen/interactive_postcard/postcard_detail_bloc.dart index 41e573292..14e4fde7d 100644 --- a/lib/screen/interactive_postcard/postcard_detail_bloc.dart +++ b/lib/screen/interactive_postcard/postcard_detail_bloc.dart @@ -71,7 +71,7 @@ class PostcardDetailBloc .where((element) => element.id == event.identity.id) .toList(); if (assetToken.isNotEmpty) { - final paths = getUpdatingPath(state, assetToken.first); + final paths = getUpdatingPath(assetToken.first); emit(state.copyWith( assetToken: assetToken.first, provenances: assetToken.first.provenance, @@ -88,7 +88,7 @@ class PostcardDetailBloc await tokenService.reindexAddresses([event.identity.owner]); final assetToken = await _assetTokenDao.findAssetTokenByIdAndOwner( event.identity.id, event.identity.owner); - final paths = getUpdatingPath(state, assetToken); + final paths = getUpdatingPath(assetToken); emit(state.copyWith( assetToken: assetToken, imagePath: paths.first, @@ -179,8 +179,7 @@ class PostcardDetailBloc } } - Pair getUpdatingPath( - PostcardDetailState state, AssetToken? asset) { + Pair getUpdatingPath(AssetToken? asset) { String? imagePath; String? metadataPath; if (asset != null) { diff --git a/lib/screen/interactive_postcard/postcard_detail_page.dart b/lib/screen/interactive_postcard/postcard_detail_page.dart index 99494609b..61fb467f3 100644 --- a/lib/screen/interactive_postcard/postcard_detail_page.dart +++ b/lib/screen/interactive_postcard/postcard_detail_page.dart @@ -46,7 +46,6 @@ import 'package:autonomy_flutter/util/share_helper.dart'; import 'package:autonomy_flutter/util/string_ext.dart'; import 'package:autonomy_flutter/util/ui_helper.dart'; import 'package:autonomy_flutter/view/artwork_common_widget.dart'; -import 'package:autonomy_flutter/view/dot_loading_indicator.dart'; import 'package:autonomy_flutter/view/external_link.dart'; import 'package:autonomy_flutter/view/postcard_button.dart'; import 'package:autonomy_flutter/view/postcard_chat.dart'; @@ -368,10 +367,7 @@ class ClaimedPostcardDetailPageState extends State .any((element) => element.id == assetToken.id && element.owner == assetToken.owner); - if (!state.isPostcardUpdating && - !state.isPostcardUpdatingOnBlockchain && - state.isStamped && - !alreadyShowPostcardUpdate) { + if (!alreadyShowPostcardUpdate) { if (_configurationService.isNotificationEnabled() != true) { _postcardUpdated(context); } @@ -605,24 +601,7 @@ class ClaimedPostcardDetailPageState extends State isViewOnly != false) { return const SizedBox(); } - if (state.isPostcardUpdatingOnBlockchain || state.isPostcardUpdating) { - return PostcardCustomButton( - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - "updating_token".tr(), - style: theme.textTheme.moMASans700Black14, - ), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: DotsLoading(), - ), - ], - ), - ); - } - if (!state.isStamped) { + if (!(asset.isStamping || asset.isStamped)) { return PostcardButton( text: "stamp_postcard".tr(), onTap: () { diff --git a/lib/screen/interactive_postcard/postcard_detail_state.dart b/lib/screen/interactive_postcard/postcard_detail_state.dart index ec3084fef..825857619 100644 --- a/lib/screen/interactive_postcard/postcard_detail_state.dart +++ b/lib/screen/interactive_postcard/postcard_detail_state.dart @@ -126,10 +126,6 @@ extension PostcardDetailStateExtension on PostcardDetailState { (isStamping() && postcardValue!.stamped == false); } - bool get isStamped { - return postcardValue?.stamped ?? false; - } - bool get isLastOwner { if (postcardValue == null) { return true; diff --git a/lib/screen/interactive_postcard/stamp_preview.dart b/lib/screen/interactive_postcard/stamp_preview.dart index 106385b97..52c2a6478 100644 --- a/lib/screen/interactive_postcard/stamp_preview.dart +++ b/lib/screen/interactive_postcard/stamp_preview.dart @@ -6,7 +6,6 @@ import 'package:autonomy_flutter/common/environment.dart'; import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/model/postcard_metadata.dart'; import 'package:autonomy_flutter/screen/app_router.dart'; -import 'package:autonomy_flutter/screen/detail/artwork_detail_page.dart'; import 'package:autonomy_flutter/screen/interactive_postcard/postcard_detail_bloc.dart'; import 'package:autonomy_flutter/screen/interactive_postcard/postcard_detail_page.dart'; import 'package:autonomy_flutter/screen/interactive_postcard/postcard_detail_state.dart'; @@ -22,7 +21,6 @@ import 'package:autonomy_flutter/util/moma_style_color.dart'; import 'package:autonomy_flutter/util/share_helper.dart'; import 'package:autonomy_flutter/util/ui_helper.dart'; import 'package:autonomy_flutter/view/back_appbar.dart'; -import 'package:autonomy_flutter/view/dot_loading_indicator.dart'; import 'package:autonomy_flutter/view/postcard_button.dart'; import 'package:autonomy_flutter/view/responsive.dart'; import 'package:autonomy_theme/autonomy_theme.dart'; @@ -74,26 +72,10 @@ class _StampPreviewState extends State { super.dispose(); } - void _refreshPostcard() { - log.info("Refresh postcard"); - context.read().add(PostcardDetailGetInfoEvent( - ArtworkIdentity(widget.payload.asset.id, widget.payload.asset.owner), - )); - } - - void _setTimer() { - timer?.cancel(); - const duration = Duration(seconds: 10); - timer = Timer.periodic(duration, (timer) { - if (mounted) { - _refreshPostcard(); - } - }); - } - Future showOptions(BuildContext context, {required AssetToken assetToken, Function()? callBack}) async { final theme = Theme.of(context); + bool isProcessing = false; final options = [ OptionItem( title: "stamp_minted".tr(), @@ -124,6 +106,7 @@ class _StampPreviewState extends State { ), ), onTap: () async { + isProcessing = true; shareToTwitter(token: assetToken); Navigator.of(context).pop(); await callBack?.call(); @@ -146,6 +129,7 @@ class _StampPreviewState extends State { ), ), onTap: () async { + isProcessing = true; try { await _postcardService.downloadStamp( tokenId: assetToken.tokenId!, @@ -171,41 +155,45 @@ class _StampPreviewState extends State { } }, ), - OptionItem( - title: 'download_postcard'.tr(), - icon: SvgPicture.asset( - 'assets/images/download.svg', + OptionItem( + title: 'download_postcard'.tr(), + icon: SvgPicture.asset( + 'assets/images/download.svg', + width: 24, + height: 24, + ), + iconOnProcessing: SvgPicture.asset('assets/images/download.svg', width: 24, height: 24, - ), - iconOnProcessing: SvgPicture.asset('assets/images/download.svg', - width: 24, - height: 24, - colorFilter: const ColorFilter.mode( - AppColor.disabledColor, BlendMode.srcIn)), - onTap: () async { - try { - await _postcardService.downloadPostcard(assetToken.tokenId!); - if (!mounted) return; - Navigator.of(context).pop(); - await UIHelper.showPostcardSaved(context); - } catch (e) { - log.info("Download postcard failed: error ${e.toString()}"); - if (!mounted) return; - Navigator.of(context).pop(); - switch (e.runtimeType) { - case MediaPermissionException: - await UIHelper.showPostcardPhotoAccessFailed(context); - break; - default: - if (!mounted) return; - await UIHelper.showPostcardSavedFailed(context); - } + colorFilter: const ColorFilter.mode( + AppColor.disabledColor, BlendMode.srcIn)), + onTap: () async { + isProcessing = true; + try { + await _postcardService.downloadPostcard(assetToken.tokenId!); + if (!mounted) return; + Navigator.of(context).pop(); + await UIHelper.showPostcardSaved(context); + } catch (e) { + log.info("Download postcard failed: error ${e.toString()}"); + if (!mounted) return; + Navigator.of(context).pop(); + switch (e.runtimeType) { + case MediaPermissionException: + await UIHelper.showPostcardPhotoAccessFailed(context); + break; + default: + if (!mounted) return; + await UIHelper.showPostcardSavedFailed(context); } - }, - ), + } + }, + ), ]; - await UIHelper.showPostcardDrawerAction(context, options: options); + await UIHelper.showPostcardDrawerAction(context, options: options) + .then((value) { + if (!isProcessing) callBack?.call(); + }); } @override @@ -250,31 +238,7 @@ class _StampPreviewState extends State { statusBarColor: backgroundColor, ), body: BlocConsumer( - listener: (context, state) { - if (!(state.isPostcardUpdatingOnBlockchain || - state.isPostcardUpdating)) { - final assetToken = state.assetToken; - if (assetToken == null) { - return; - } - timer?.cancel(); - if (alreadyShowPopup) { - return; - } - alreadyShowPopup = true; - showOptions(context, assetToken: assetToken, callBack: () { - log.info("Popup closed"); - _navigationService.popUntilHomeOrSettings(); - if (!mounted) return; - Navigator.of(context).pushNamed( - AppRouter.claimedPostcardDetailsPage, - arguments: - PostcardDetailPagePayload([assetToken.identity], 0), - ); - _configurationService.setAutoShowPostcard(true); - }); - } - }, + listener: (context, state) {}, builder: (context, state) { final assetToken = widget.payload.asset; final imagePath = widget.payload.imagePath; @@ -305,8 +269,24 @@ class _StampPreviewState extends State { ); } + Future onConfirmed(AssetToken assetToken) async { + if (alreadyShowPopup) { + return; + } + alreadyShowPopup = true; + showOptions(context, assetToken: assetToken, callBack: () { + log.info("Popup closed"); + if (!mounted) return; + _navigationService.popUntilHomeOrSettings(); + Navigator.of(context).pushNamed( + AppRouter.claimedPostcardDetailsPage, + arguments: PostcardDetailPagePayload([assetToken.identity], 0), + ); + _configurationService.setAutoShowPostcard(true); + }); + } + Widget _postcardAction(PostcardDetailState state) { - final theme = Theme.of(context); if (!confirming) { return PostcardAsyncButton( text: widget.payload.asset.isCompleted @@ -314,36 +294,20 @@ class _StampPreviewState extends State { : "confirm_your_design".tr(), fontSize: 18, onTap: () async { - final isSuccess = await _onConfirm(); - if (mounted && isSuccess) { + final assetToken = await _onConfirm(); + if (mounted && assetToken != null) { setState(() { confirming = true; }); + await onConfirmed(assetToken); } }, ); } - if (state.isPostcardUpdatingOnBlockchain || state.isPostcardUpdating) { - return PostcardCustomButton( - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - "updating_token".tr(), - style: theme.textTheme.moMASans700Black14.copyWith(fontSize: 18), - ), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: DotsLoading(), - ), - ], - ), - ); - } return const SizedBox(); } - Future _onConfirm() async { + Future _onConfirm() async { final imagePath = widget.payload.imagePath; final metadataPath = widget.payload.metadataPath; File imageFile = File(imagePath); @@ -358,7 +322,7 @@ class _StampPreviewState extends State { final walletIndex = await asset.getOwnerWallet(); if (walletIndex == null) { log.info("[POSTCARD] Wallet index not found"); - return false; + return null; } final isStampSuccess = await _postcardService.stampPostcard( @@ -373,7 +337,7 @@ class _StampPreviewState extends State { if (!isStampSuccess) { log.info("[POSTCARD] Stamp failed"); injector().popUntilHomeOrSettings(); - return false; + return null; } else { log.info("[POSTCARD] Stamp success"); _metricClientService.addEvent(MixpanelEvent.postcardStamp, data: { @@ -389,23 +353,22 @@ class _StampPreviewState extends State { counter: counter, ) ]); - + AssetToken? pendingToken; if (widget.payload.location != null) { var postcardMetadata = asset.postcardMetadata; final stampedLocation = widget.payload.location!; postcardMetadata.locationInformation.add(stampedLocation); var newAsset = asset.asset; newAsset?.artworkMetadata = jsonEncode(postcardMetadata.toJson()); - final pendingToken = asset.copyWith(asset: newAsset); + pendingToken = asset.copyWith(asset: newAsset); await _tokenService.setCustomTokens([pendingToken]); _tokenService.reindexAddresses([address]); NftCollectionBloc.eventController.add( GetTokensByOwnerEvent(pageKey: PageKey.init()), ); - _setTimer(); } + return pendingToken; } - return true; } } diff --git a/lib/screen/interactive_postcard/travel_info/postcard_travel_info.dart b/lib/screen/interactive_postcard/travel_info/postcard_travel_info.dart index 9bd99ae53..06c666454 100644 --- a/lib/screen/interactive_postcard/travel_info/postcard_travel_info.dart +++ b/lib/screen/interactive_postcard/travel_info/postcard_travel_info.dart @@ -36,7 +36,8 @@ class _PostcardTravelInfoState extends State { final theme = Theme.of(context); final travelInfoWithoutInternetUser = asset.postcardMetadata.listTravelInfoWithoutLocationName; - final currentStampNumber = asset.getArtists.length; + final stampingPostcard = asset.isStamping ? 1 : 0; + final currentStampNumber = asset.getArtists.length + stampingPostcard; final numberFormatter = NumberFormat(); return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/screen/send_receive_postcard/receive_postcard_page.dart b/lib/screen/send_receive_postcard/receive_postcard_page.dart index 028164f0d..18bb9bb20 100644 --- a/lib/screen/send_receive_postcard/receive_postcard_page.dart +++ b/lib/screen/send_receive_postcard/receive_postcard_page.dart @@ -9,6 +9,7 @@ import 'package:autonomy_flutter/service/account_service.dart'; import 'package:autonomy_flutter/service/metric_client_service.dart'; import 'package:autonomy_flutter/service/navigation_service.dart'; import 'package:autonomy_flutter/service/postcard_service.dart'; +import 'package:autonomy_flutter/util/asset_token_ext.dart'; import 'package:autonomy_flutter/util/constants.dart'; import 'package:autonomy_flutter/util/ui_helper.dart'; import 'package:autonomy_flutter/util/wallet_utils.dart'; @@ -18,6 +19,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nft_collection/models/models.dart'; +import 'package:nft_collection/services/tokens_service.dart'; class ReceivePostcardPageArgs { final AssetToken asset; @@ -44,13 +46,19 @@ class ReceivePostCardPage extends StatefulWidget { class _ReceivePostCardPageState extends State { final metricClient = injector.get(); + final tokenService = injector.get(); late bool _isProcessing; + late bool _isConfirming; + late AssetToken assetToken; @override void initState() { _fetchIdentities(); super.initState(); _isProcessing = false; + _isConfirming = !widget.asset.isStamped; + assetToken = widget.asset; + _waitUntilPostcardConfirm(); } void _fetchIdentities() { @@ -66,20 +74,21 @@ class _ReceivePostCardPageState extends State { @override Widget build(BuildContext context) { - final asset = widget.asset; return PostcardExplain( payload: PostcardExplainPayload( - asset, + assetToken, PostcardButton( - text: "continue".tr(), + text: (_isConfirming) + ? "confirming_on_blockchain_".tr() + : "continue".tr(), fontSize: 18, - enabled: !(_isProcessing), + enabled: !(_isProcessing || _isConfirming), isProcessing: _isProcessing, onTap: () async { setState(() { _isProcessing = true; }); - await _receivePostcard(context, asset); + await _receivePostcard(context, assetToken); }, color: const Color.fromRGBO(79, 174, 79, 1), ), @@ -87,6 +96,21 @@ class _ReceivePostCardPageState extends State { ); } + Future _waitUntilPostcardConfirm() async { + final tokenId = widget.asset.id; + bool isExit = false; + while (!isExit) { + final postcard = await injector().getPostcard(tokenId); + if (postcard.isStamped) { + setState(() { + _isConfirming = false; + assetToken = postcard; + }); + return postcard; + } + } + } + Future _receivePostcard( BuildContext context, AssetToken asset) async { final geoLocation = internetUserGeoLocation; diff --git a/lib/service/postcard_service.dart b/lib/service/postcard_service.dart index 7f9a01e26..548374ccb 100644 --- a/lib/service/postcard_service.dart +++ b/lib/service/postcard_service.dart @@ -520,9 +520,7 @@ class PostcardServiceImpl extends PostcardService { }); final tokenID = 'tez-${result.contractAddress}-${result.tokenID}'; final postcardMetadata = PostcardMetadata( - locationInformation: [ - moMAGeoLocation.position, - ], + locationInformation: [], ); final token = AssetToken( asset: Asset.init( diff --git a/lib/util/asset_token_ext.dart b/lib/util/asset_token_ext.dart index 29871aa57..81198b839 100644 --- a/lib/util/asset_token_ext.dart +++ b/lib/util/asset_token_ext.dart @@ -9,6 +9,7 @@ import 'package:autonomy_flutter/model/postcard_metadata.dart'; import 'package:autonomy_flutter/screen/detail/artwork_detail_page.dart'; import 'package:autonomy_flutter/screen/interactive_postcard/stamp_preview.dart'; import 'package:autonomy_flutter/service/configuration_service.dart'; +import 'package:autonomy_flutter/service/postcard_service.dart'; import 'package:autonomy_flutter/util/constants.dart'; import 'package:autonomy_flutter/util/feralfile_extension.dart'; import 'package:autonomy_flutter/util/log.dart'; @@ -637,6 +638,15 @@ extension PostcardExtension on AssetToken { return maxEdition ?? 0; } + bool get isStamping { + final stampingPostcard = injector().getStampingPostcard(); + return stampingPostcard.any((element) { + final bool = + (element.indexId == id && element.address == owner && isLastOwner); + return bool; + }); + } + bool get isStamped { return numberOwners == getArtists.length; }