diff --git a/.github/workflows/android-release-appcenter.yaml b/.github/workflows/android-release-appcenter.yaml index fa5f9c68c..0d051cc8a 100644 --- a/.github/workflows/android-release-appcenter.yaml +++ b/.github/workflows/android-release-appcenter.yaml @@ -76,6 +76,7 @@ jobs: echo TZKT_MAINNET_URL=${{ secrets.TZKT_MAINNET_URL }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_TESTNET }} >> .env || echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_MAINNET }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_MAINNET }} >> .env + ${{ github.event.inputs.testnet == 'true' }} && echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_MAINNET }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_TESTNET }} >> .env || echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_MAINNET }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_DEV }} >> .env || echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_PRD }} >> .env diff --git a/.github/workflows/bmvn_build_appcenter_android.yaml b/.github/workflows/bmvn_build_appcenter_android.yaml index 1726448d3..c190c43de 100644 --- a/.github/workflows/bmvn_build_appcenter_android.yaml +++ b/.github/workflows/bmvn_build_appcenter_android.yaml @@ -95,6 +95,7 @@ jobs: echo TZKT_MAINNET_URL=${{ secrets.TZKT_MAINNET_URL }} >> .env ${{ inputs.testnet == true }} && echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_TESTNET }} >> .env || echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_MAINNET }} >> .env ${{ inputs.testnet == true }} && echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_MAINNET }} >> .env + ${{ inputs.testnet == true }} && echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_MAINNET }} >> .env ${{ inputs.testnet == true }} && echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_TESTNET }} >> .env || echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_MAINNET }} >> .env ${{ inputs.testnet == true }} && echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_DEV }} >> .env || echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_PRD }} >> .env diff --git a/.github/workflows/bmvn_build_appcenter_ios.yaml b/.github/workflows/bmvn_build_appcenter_ios.yaml index 59c90acf4..aa92b54be 100644 --- a/.github/workflows/bmvn_build_appcenter_ios.yaml +++ b/.github/workflows/bmvn_build_appcenter_ios.yaml @@ -102,6 +102,7 @@ jobs: echo TZKT_MAINNET_URL=${{ secrets.TZKT_MAINNET_URL }} >> .env ${{ inputs.testnet == true }} && echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_TESTNET }} >> .env || echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_MAINNET }} >> .env ${{ inputs.testnet == true }} && echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_MAINNET }} >> .env + ${{ inputs.testnet == true }} && echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_MAINNET }} >> .env ${{ inputs.testnet == true }} && echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_TESTNET }} >> .env || echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_MAINNET }} >> .env ${{ inputs.testnet == true }} && echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_DEV }} >> .env || echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_PRD }} >> .env diff --git a/.github/workflows/ios-release-appstore.yaml b/.github/workflows/ios-release-appstore.yaml index 7f1053344..03a0f49fa 100644 --- a/.github/workflows/ios-release-appstore.yaml +++ b/.github/workflows/ios-release-appstore.yaml @@ -87,6 +87,7 @@ jobs: echo TZKT_MAINNET_URL=${{ secrets.TZKT_MAINNET_URL }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_TESTNET }} >> .env || echo POSTCARD_CONTRACT_ADDRESS=${{ secrets.POSTCARD_CONTRACT_ADDRESS_MAINNET }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_BASE_URL=${{ secrets.AUTONOMY_MERCHANDISE_BASE_URL_MAINNET }} >> .env + ${{ github.event.inputs.testnet == 'true' }} && echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_TESTNET }} >> .env || echo AUTONOMY_MERCHANDISE_API_URL=${{ secrets.AUTONOMY_MERCHANDISE_API_URL_MAINNET }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_TESTNET }} >> .env || echo PAY_TO_MINT_BASE_URL=${{ secrets.PAY_TO_MINT_BASE_URL_MAINNET }} >> .env ${{ github.event.inputs.testnet == 'true' }} && echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_DEV }} >> .env || echo AUTONOMY_PUBDOC_URL=${{ secrets.AUTONOMY_PUBDOC_URL_PRD }} >> .env diff --git a/lib/common/injector.dart b/lib/common/injector.dart index 2e8e68695..84476e51a 100644 --- a/lib/common/injector.dart +++ b/lib/common/injector.dart @@ -5,6 +5,8 @@ // that can be found in the LICENSE file. // +// ignore_for_file: cascade_invocations + import 'dart:math'; import 'package:autonomy_flutter/common/environment.dart'; diff --git a/lib/gateway/postcard_api.dart b/lib/gateway/postcard_api.dart index 8899a1d5c..0493742a0 100644 --- a/lib/gateway/postcard_api.dart +++ b/lib/gateway/postcard_api.dart @@ -31,6 +31,9 @@ abstract class PostcardApi { @GET('/v1/postcard/claim/{share_code}') Future claimShareCode(@Path('share_code') String shareCode); + @GET('/v1/postcard/{token_id}/merchandise_enabled') + Future getMerchandiseEnable(@Path('token_id') String tokenId); + @MultiPart() @POST('/v1/postcard/{token_id}/stamp') Future updatePostcard({ diff --git a/lib/gateway/postcard_api.g.dart b/lib/gateway/postcard_api.g.dart index 5ab77f45a..101aa0899 100644 --- a/lib/gateway/postcard_api.g.dart +++ b/lib/gateway/postcard_api.g.dart @@ -158,6 +158,32 @@ class _PostcardApi implements PostcardApi { return value; } + @override + Future getMerchandiseEnable(String tokenId) async { + const _extra = {}; + final queryParameters = {}; + final _headers = {}; + final Map? _data = null; + final _result = await _dio.fetch(_setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/v1/postcard/${tokenId}/merchandise_enabled', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final value = _result.data!; + return value; + } + @override Future updatePostcard({ required String tokenId, diff --git a/lib/screen/app_router.dart b/lib/screen/app_router.dart index 961a1752b..10d5def54 100644 --- a/lib/screen/app_router.dart +++ b/lib/screen/app_router.dart @@ -220,6 +220,16 @@ class AppRouter { injector(), ); final identityBloc = IdentityBloc(injector(), injector()); + final postcardDetailBloc = PostcardDetailBloc( + injector(), + injector(), + injector(), + injector(), + injector(), + injector(), + injector(), + injector(), + ); switch (settings.name) { case viewPlayListPage: @@ -382,15 +392,7 @@ class AppRouter { child: MultiBlocProvider( providers: [ BlocProvider(create: (_) => identityBloc), - BlocProvider( - create: (_) => PostcardDetailBloc( - injector(), - injector(), - injector(), - injector(), - injector(), - injector(), - )), + BlocProvider(create: (_) => postcardDetailBloc), ], child: StampPreview( payload: settings.arguments! as StampPreviewPayload), @@ -587,9 +589,7 @@ class AppRouter { injector(), ), ), - BlocProvider( - create: (_) => PostcardDetailBloc(injector(), injector(), - injector(), injector(), injector(), injector())), + BlocProvider(create: (_) => postcardDetailBloc), ], child: ArtworkPreviewPage( payload: settings.arguments! as ArtworkDetailPayload, @@ -683,15 +683,7 @@ class AppRouter { BlocProvider.value(value: accountsBloc), BlocProvider(create: (_) => identityBloc), BlocProvider(create: (_) => TravelInfoBloc()), - BlocProvider( - create: (_) => PostcardDetailBloc( - injector(), - injector(), - injector(), - injector(), - injector(), - injector(), - )), + BlocProvider(create: (_) => postcardDetailBloc), ], child: ClaimedPostcardDetailPage( key: payload.key, payload: payload))); @@ -1132,16 +1124,7 @@ class AppRouter { child: MultiBlocProvider( providers: [ BlocProvider.value(value: accountsBloc), - BlocProvider( - create: (_) => PostcardDetailBloc( - injector(), - injector(), - injector(), - injector(), - injector(), - injector(), - ), - ), + BlocProvider(create: (_) => postcardDetailBloc), ], child: PostcardLeaderboardPage( payload: settings.arguments! as PostcardLeaderboardPagePayload, diff --git a/lib/screen/detail/preview_detail/preview_detail_widget.dart b/lib/screen/detail/preview_detail/preview_detail_widget.dart index ead02618c..f10f43552 100644 --- a/lib/screen/detail/preview_detail/preview_detail_widget.dart +++ b/lib/screen/detail/preview_detail/preview_detail_widget.dart @@ -194,8 +194,8 @@ class PostcardPreviewWidget extends StatefulWidget { class _PostcardPreviewWidgetState extends State with WidgetsBindingObserver, RouteAware { - final bloc = PostcardDetailBloc( - injector(), injector(), injector(), injector(), injector(), injector()); + final bloc = PostcardDetailBloc(injector(), injector(), injector(), + injector(), injector(), injector(), injector(), injector()); @override void initState() { diff --git a/lib/screen/interactive_postcard/claim_empty_postcard/claim_empty_postcard_bloc.dart b/lib/screen/interactive_postcard/claim_empty_postcard/claim_empty_postcard_bloc.dart index 367fe7d1e..2b26ecdb3 100644 --- a/lib/screen/interactive_postcard/claim_empty_postcard/claim_empty_postcard_bloc.dart +++ b/lib/screen/interactive_postcard/claim_empty_postcard/claim_empty_postcard_bloc.dart @@ -5,7 +5,6 @@ import 'package:autonomy_flutter/common/environment.dart'; import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/model/postcard_claim.dart'; import 'package:autonomy_flutter/model/postcard_metadata.dart'; -import 'package:autonomy_flutter/model/prompt.dart'; import 'package:autonomy_flutter/screen/app_router.dart'; import 'package:autonomy_flutter/service/account_service.dart'; import 'package:autonomy_flutter/service/configuration_service.dart'; @@ -35,13 +34,7 @@ class ClaimEmptyPostCardBloc on((event, emit) async { final indexId = event.claimRequest.tokenId; final tokenId = 'tez-${Environment.postcardContractAddress}-$indexId'; - late final Prompt? prompt; - try { - final prompts = await _postcardService.getPrompts(indexId); - prompt = prompts.isNotEmpty ? prompts.first : null; - } catch (e) { - prompt = null; - } + final prompt = await _postcardService.getPrompt(indexId); final postcardMetadata = PostcardMetadata( prompt: prompt, locationInformation: [], diff --git a/lib/screen/interactive_postcard/postcard_detail_bloc.dart b/lib/screen/interactive_postcard/postcard_detail_bloc.dart index 18e590b0a..83212fcf5 100644 --- a/lib/screen/interactive_postcard/postcard_detail_bloc.dart +++ b/lib/screen/interactive_postcard/postcard_detail_bloc.dart @@ -8,13 +8,13 @@ import 'dart:async'; import 'package:autonomy_flutter/au_bloc.dart'; -import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/model/pair.dart'; import 'package:autonomy_flutter/screen/detail/artwork_detail_page.dart'; import 'package:autonomy_flutter/screen/interactive_postcard/leaderboard/postcard_leaderboard.dart'; import 'package:autonomy_flutter/screen/interactive_postcard/postcard_detail_state.dart'; import 'package:autonomy_flutter/service/configuration_service.dart'; import 'package:autonomy_flutter/service/postcard_service.dart'; +import 'package:autonomy_flutter/service/remote_config_service.dart'; import 'package:autonomy_flutter/util/asset_token_ext.dart'; import 'package:autonomy_flutter/util/constants.dart'; import 'package:autonomy_flutter/util/distance_formater.dart'; @@ -47,6 +47,8 @@ class PostcardDetailBloc final IndexerService _indexerService; final PostcardService _postcardService; final ConfigurationService _configurationService; + final TokensService _tokenService; + final RemoteConfigService _remoteConfig; PostcardDetailBloc( this._assetTokenDao, @@ -55,6 +57,8 @@ class PostcardDetailBloc this._indexerService, this._postcardService, this._configurationService, + this._tokenService, + this._remoteConfig, ) : super(PostcardDetailState(provenances: [])) { on((event, emit) async { if (event.useIndexer) { @@ -72,17 +76,19 @@ class PostcardDetailBloc assetToken.first.setAssetPrompt(tempsPrompt); } final paths = getUpdatingPath(assetToken.first); + emit(state.copyWith( assetToken: assetToken.first, provenances: assetToken.first.provenance, imagePath: paths.first, metadataPath: paths.second, + showMerch: false, + enableMerch: false, )); } return; } else { - final tokenService = injector(); - unawaited(tokenService.reindexAddresses([event.identity.owner])); + unawaited(_tokenService.reindexAddresses([event.identity.owner])); final assetToken = await _assetTokenDao.findAssetTokenByIdAndOwner( event.identity.id, event.identity.owner); if (assetToken == null) { @@ -96,14 +102,30 @@ class PostcardDetailBloc assetToken?.setAssetPrompt(tempsPrompt); } final paths = getUpdatingPath(assetToken); - emit(state.copyWith( - assetToken: assetToken, - imagePath: paths.first, - metadataPath: paths.second)); + final isViewOnly = await assetToken?.isViewOnly(); + emit( + state.copyWith( + assetToken: assetToken, + imagePath: paths.first, + metadataPath: paths.second, + isViewOnly: isViewOnly), + ); - final provenances = - await _provenanceDao.findProvenanceByTokenID(event.identity.id); - emit(state.copyWith(provenances: provenances)); + final showProvenances = + _remoteConfig.getBool(ConfigGroup.viewDetail, ConfigKey.provenance); + if (showProvenances) { + final provenances = + await _provenanceDao.findProvenanceByTokenID(event.identity.id); + emit(state.copyWith(provenances: provenances)); + } + + final showMerch = + await _showMerchProduct(assetToken, isViewOnly ?? true); + if (showMerch != state.showMerch) { + emit(state.copyWith( + showMerch: showMerch, + enableMerch: assetToken?.enabledMerch ?? false)); + } if (assetToken != null && assetToken.asset != null && @@ -167,9 +189,8 @@ class PostcardDetailBloc String? imagePath; String? metadataPath; if (asset != null) { - final postcardService = injector(); final stampingPostcard = - postcardService.getStampingPostcardWithPath(asset.stampingPostcard!); + _postcardService.getStampingPostcardWithPath(asset.stampingPostcard!); final processingStampPostcard = asset.processingStampPostcard; final isStamped = asset.isStamped; if (!isStamped) { @@ -186,7 +207,7 @@ class PostcardDetailBloc } } else { if (stampingPostcard != null) { - unawaited(postcardService + unawaited(_postcardService .updateStampingPostcard([stampingPostcard], isRemove: true)); } if (processingStampPostcard != null) { @@ -198,4 +219,25 @@ class PostcardDetailBloc } return Pair(imagePath, metadataPath); } + + Future _showMerchProduct(AssetToken? asset, bool isViewOnly) async { + if (asset == null) { + return false; + } + final isShowConfig = + _remoteConfig.getBool(ConfigGroup.merchandise, ConfigKey.enable) && + (_remoteConfig.getBool( + ConfigGroup.merchandise, ConfigKey.allowViewOnly) || + !isViewOnly); + if (!isShowConfig) { + return false; + } + try { + final enableMerch = + await _postcardService.isMerchandiseEnable(asset.tokenId ?? ''); + return enableMerch; + } catch (e) { + return false; + } + } } diff --git a/lib/screen/interactive_postcard/postcard_detail_page.dart b/lib/screen/interactive_postcard/postcard_detail_page.dart index 77b62910c..2e64f7fe8 100644 --- a/lib/screen/interactive_postcard/postcard_detail_page.dart +++ b/lib/screen/interactive_postcard/postcard_detail_page.dart @@ -98,7 +98,7 @@ class ClaimedPostcardDetailPageState extends State with AfterLayoutMixin { late ScrollController _scrollController; late bool withSharing; - late bool isViewOnly; + late bool isNotOwner; late bool isSending; late bool alreadyShowPopup; late bool isProcessingStampPostcard; @@ -118,7 +118,7 @@ class ClaimedPostcardDetailPageState extends State @override void initState() { _scrollController = ScrollController(); - isViewOnly = widget.payload.isFromLeaderboard; + isNotOwner = widget.payload.isFromLeaderboard; isSending = false; alreadyShowPopup = false; isProcessingStampPostcard = false; @@ -127,8 +127,8 @@ class ClaimedPostcardDetailPageState extends State context.read().add( PostcardDetailGetInfoEvent( widget.payload.identities[widget.payload.currentIndex], - useIndexer: widget.payload.isFromLeaderboard || - widget.payload.useIndexer), + useIndexer: widget.payload.useIndexer || + widget.payload.isFromLeaderboard), ); context.read().add(FetchLeaderboardEvent()); context.read().add(FetchAllAddressesEvent()); @@ -369,7 +369,8 @@ class ClaimedPostcardDetailPageState extends State if (previous.assetToken?.isCompleted != true && current.assetToken?.isCompleted == true && current.assetToken?.isAlreadyShowYouDidIt == false && - !isViewOnly) { + !isNotOwner && + current.isViewOnly == false) { unawaited(_youDidIt(context, current.assetToken!)); } return true; @@ -387,8 +388,8 @@ class ClaimedPostcardDetailPageState extends State return; } final assetToken = state.assetToken; + if (assetToken != null) { - final viewOnly = isViewOnly || (await assetToken.isViewOnly()); if (!mounted) { return; } @@ -405,10 +406,10 @@ class ClaimedPostcardDetailPageState extends State } setState(() { currentAsset = state.assetToken; - isViewOnly = viewOnly; + isNotOwner = isNotOwner || (state.isViewOnly ?? true); isSending = state.assetToken?.isSending ?? false; }); - if (viewOnly) { + if (isNotOwner) { return; } if (withSharing) { @@ -451,7 +452,7 @@ class ClaimedPostcardDetailPageState extends State } if (assetToken.isShareExpired && - (assetToken.isLastOwner && !isViewOnly)) { + (assetToken.isLastOwner && !isNotOwner)) { if (!mounted) { return; } @@ -590,9 +591,7 @@ class ClaimedPostcardDetailPageState extends State }, ), ), - const SizedBox( - height: 15, - ), + const SizedBox(height: 15), Hero( tag: 'detail_${asset.id}', child: Stack( @@ -620,55 +619,34 @@ class ClaimedPostcardDetailPageState extends State ], ), ), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), if (_remoteConfig.getBool(ConfigGroup.viewDetail, ConfigKey.actionButton)) ...[ _postcardAction(context, asset), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), ], - if ((asset.isCompleted || - !_remoteConfig.getBool( - ConfigGroup.merchandise, - ConfigKey.mustCompleted)) && - _remoteConfig.getBool(ConfigGroup.merchandise, - ConfigKey.enable) && - (_remoteConfig.getBool(ConfigGroup.merchandise, - ConfigKey.allowViewOnly) || - !isViewOnly)) ...[ - _postcardPhysical(context, asset), - const SizedBox( - height: 20, - ), + if (state.showMerch == true) ...[ + _postcardPhysical( + context, asset, state.enableMerch ?? false), + const SizedBox(height: 20), ], _postcardInfo(context, asset), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), if (_remoteConfig.getBool(ConfigGroup.viewDetail, ConfigKey.leaderBoard)) ...[ _postcardLeaderboard( context, state.leaderboard, asset), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), ], if (_remoteConfig.getBool(ConfigGroup.viewDetail, ConfigKey.aboutMoma)) ...[ _aboutTheProject(context), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), ], if (_remoteConfig.getBool(ConfigGroup.viewDetail, ConfigKey.glossary)) ...[ _web3Glossary(context, asset), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), ], _artworkInfo(context, asset, state.provenances, artistNames, owners), @@ -701,11 +679,7 @@ class ClaimedPostcardDetailPageState extends State Widget _postcardAction(final BuildContext context, final AssetToken asset) { final theme = Theme.of(context); - final place15StampsText = Text( - 'place_15_stamps'.tr(), - style: theme.textTheme.moMASans400Black12, - ); - if (asset.isCompleted || isViewOnly || !asset.isLastOwner) { + if (asset.isCompleted || isNotOwner || !asset.isLastOwner) { return const SizedBox(); } if (isProcessingStampPostcard || @@ -757,10 +731,6 @@ class ClaimedPostcardDetailPageState extends State style: theme.textTheme.moMASans400Black12, ), ), - Padding( - padding: const EdgeInsets.only(left: 16, right: 15, top: 10), - child: place15StampsText, - ), ]; if (!asset.isFinal) { if (!isSending) { @@ -811,12 +781,19 @@ class ClaimedPostcardDetailPageState extends State return const SizedBox(); } - Widget _postcardPhysical(BuildContext context, AssetToken assetToken) => + Widget _postcardPhysical( + BuildContext context, AssetToken assetToken, bool isEnable) => Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ PostcardButton( - text: 'unlock_physical_objects'.tr(), - color: POSTCARD_PINK_BUTTON_COLOR, + text: 'buy_merch'.tr(), + enabled: isEnable, + icon: SvgPicture.asset( + isEnable + ? 'assets/images/unlock_icon.svg' + : 'assets/images/lock_icon.svg', + ), onTap: () async { final indexId = assetToken.id; final jwtToken = @@ -834,6 +811,16 @@ class ClaimedPostcardDetailPageState extends State isPlainUI: true, localStorageItems: {'token': jwtToken})); }, ), + if (!isEnable) ...[ + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.only(left: 16, right: 15), + child: Text( + 'must_complete_to_unlock'.tr(), + style: Theme.of(context).textTheme.moMASans400Black12, + ), + ), + ] ], ); @@ -963,8 +950,7 @@ class ClaimedPostcardDetailPageState extends State ), ), const SizedBox(height: 20), - ] else if (_remoteConfig.getBool( - ConfigGroup.viewDetail, ConfigKey.provenance)) ...[ + ] else ...[ if (provenances.isNotEmpty) PostcardContainer( child: _provenanceView(context, provenances)) @@ -1002,7 +988,6 @@ class ClaimedPostcardDetailPageState extends State Future _showArtworkOptionsDialog( BuildContext context, AssetToken asset) async { final theme = Theme.of(context); - final isViewOnly = await asset.isViewOnly(); if (!mounted) { return; } @@ -1045,7 +1030,7 @@ class ClaimedPostcardDetailPageState extends State Navigator.of(context).pop(); }, ), - if (!isViewOnly) ...[ + if (!isNotOwner) ...[ if (_remoteConfig.getBool( ConfigGroup.feature, ConfigKey.downloadStamp)) OptionItem( diff --git a/lib/screen/interactive_postcard/postcard_detail_state.dart b/lib/screen/interactive_postcard/postcard_detail_state.dart index e5b10bbf6..07e5c4669 100644 --- a/lib/screen/interactive_postcard/postcard_detail_state.dart +++ b/lib/screen/interactive_postcard/postcard_detail_state.dart @@ -17,14 +17,20 @@ class PostcardDetailState { String? metadataPath; PostcardLeaderboard? leaderboard; bool isFetchingLeaderboard; + bool? showMerch; + bool? enableMerch; + bool? isViewOnly; PostcardDetailState({ required this.provenances, + this.isViewOnly, this.assetToken, this.imagePath, this.metadataPath, this.leaderboard, this.isFetchingLeaderboard = false, + this.showMerch, + this.enableMerch, }); ArtworkDetailState toArtworkDetailState() => ArtworkDetailState( @@ -39,6 +45,9 @@ class PostcardDetailState { String? metadataPath, PostcardLeaderboard? leaderboard, bool? isFetchingLeaderboard, + bool? showMerch, + bool? isViewOnly, + bool? enableMerch, }) => PostcardDetailState( assetToken: assetToken ?? this.assetToken, @@ -48,5 +57,8 @@ class PostcardDetailState { leaderboard: leaderboard ?? this.leaderboard, isFetchingLeaderboard: isFetchingLeaderboard ?? this.isFetchingLeaderboard, + showMerch: showMerch ?? this.showMerch, + isViewOnly: isViewOnly ?? this.isViewOnly, + enableMerch: enableMerch ?? this.enableMerch, ); } diff --git a/lib/screen/interactive_postcard/postcard_explain.dart b/lib/screen/interactive_postcard/postcard_explain.dart index 21c4d1930..0c0435b8d 100644 --- a/lib/screen/interactive_postcard/postcard_explain.dart +++ b/lib/screen/interactive_postcard/postcard_explain.dart @@ -104,7 +104,7 @@ class _PostcardExplainState extends State { padding: const EdgeInsets.only(bottom: 32), child: Column( children: [ - const SizedBox(height: 25), + const SizedBox(height: 40), Padding( padding: const EdgeInsets.only(left: 15, right: 15), child: Row( @@ -138,7 +138,7 @@ class _PostcardExplainState extends State { ], ), ), - const SizedBox(height: 80), + const SizedBox(height: 25), Expanded( child: _viewClaimPage ? Padding( @@ -157,7 +157,7 @@ class _PostcardExplainState extends State { }); }, itemBuilder: (context, index) => Padding( - padding: padding, + padding: padding.copyWith(top: 40), child: pages[index], ), itemCount: swiperSize, @@ -223,12 +223,12 @@ class _PostcardExplainState extends State { children: [ Text( 'moma_project_invite'.tr(), - style: theme.textTheme.moMASans400Black18, + style: theme.textTheme.moMASans400Black14, ), - const SizedBox(height: 8), + const SizedBox(height: 20), Text( 'with_15_blank_stamps'.tr(), - style: theme.textTheme.moMASans400Black18, + style: theme.textTheme.moMASans400Black14, ), const SizedBox(height: 40), Text.rich( diff --git a/lib/service/postcard_service.dart b/lib/service/postcard_service.dart index 5ab6f3345..818d9094f 100644 --- a/lib/service/postcard_service.dart +++ b/lib/service/postcard_service.dart @@ -92,6 +92,8 @@ abstract class PostcardService { required int stampIndex, }); + Future isMerchandiseEnable(String tokenId); + Future downloadPostcard(String tokenId); String getTokenId(String id); @@ -113,7 +115,7 @@ abstract class PostcardService { required String? shareCode, }); - Future> getPrompts(String tokenId); + Future getPrompt(String tokenId); } class PostcardServiceImpl extends PostcardService { @@ -384,6 +386,13 @@ class PostcardServiceImpl extends PostcardService { await sharePostcard(asset); } + @override + Future isMerchandiseEnable(String tokenId) async { + final response = await _postcardApi.getMerchandiseEnable(tokenId); + final result = jsonDecode(response)['enabled'] as bool; + return result; + } + @override Future fetchPostcardLeaderboard( {required String unit, required int size, required int offset}) async { @@ -532,7 +541,9 @@ class PostcardServiceImpl extends PostcardService { 'postcardId': result.tokenID, })); final tokenID = 'tez-${result.contractAddress}-${result.tokenID}'; + final prompt = await getPrompt(result.tokenID ?? ''); final postcardMetadata = PostcardMetadata( + prompt: prompt, locationInformation: [], ); final token = AssetToken( @@ -544,7 +555,7 @@ class PostcardServiceImpl extends PostcardService { title: requestPostcardResponse.name, previewURL: requestPostcardResponse.previewURL, source: 'postcard', - artworkMetadata: jsonEncode(postcardMetadata.toJson()), + artworkMetadata: jsonEncode(postcardMetadata), medium: 'software', ), blockchain: 'tezos', @@ -679,10 +690,15 @@ class PostcardServiceImpl extends PostcardService { } @override - Future> getPrompts(String tokenId) async { - final prompts = await _postcardApi.getPrompts(tokenId); - log.info('[POSTCARD] getPrompts: $prompts'); - return prompts; + Future getPrompt(String tokenId) async { + try { + final prompts = await _postcardApi.getPrompts(tokenId); + log.info('[POSTCARD] getPrompts: $prompts'); + final prompt = prompts.isNotEmpty ? prompts.first : null; + return prompt; + } catch (e) { + return null; + } } } diff --git a/lib/util/asset_token_ext.dart b/lib/util/asset_token_ext.dart index 7894b3002..c05a997d2 100644 --- a/lib/util/asset_token_ext.dart +++ b/lib/util/asset_token_ext.dart @@ -15,6 +15,7 @@ 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/service/remote_config_service.dart'; import 'package:autonomy_flutter/util/constants.dart'; import 'package:autonomy_flutter/util/feralfile_extension.dart'; import 'package:autonomy_flutter/util/log.dart'; @@ -768,4 +769,11 @@ extension PostcardExtension on AssetToken { return sharedPostcards.any((element) => element.owner == owner && element.tokenID == id && element.isExpired); } + + bool get enabledMerch { + final remoteConfig = injector(); + final isEnable = isCompleted || + !remoteConfig.getBool(ConfigGroup.merchandise, ConfigKey.mustCompleted); + return isEnable; + } } diff --git a/lib/view/postcard_button.dart b/lib/view/postcard_button.dart index 9d9d32d6b..eb64267ed 100644 --- a/lib/view/postcard_button.dart +++ b/lib/view/postcard_button.dart @@ -16,6 +16,7 @@ class PostcardButton extends StatelessWidget { final Color? disabledTextColor; final double? fontSize; final TextStyle? textStyle; + final Widget? icon; const PostcardButton({ super.key, @@ -30,6 +31,7 @@ class PostcardButton extends StatelessWidget { this.disabledTextColor, this.fontSize, this.textStyle, + this.icon, }); @override @@ -69,9 +71,12 @@ class PostcardButton extends StatelessWidget { backgroundColor: theme.colorScheme.surface, strokeWidth: 2, ), - ) - else - const SizedBox(), + ), + if (icon != null) + Padding( + padding: const EdgeInsets.only(right: 8), + child: icon, + ), Text( text ?? '', style: (textStyle ?? theme.textTheme.moMASans700Black18) diff --git a/test/services/postcard_service_test.mocks.dart b/test/services/postcard_service_test.mocks.dart index b364ddd79..aa6a6ce6d 100644 --- a/test/services/postcard_service_test.mocks.dart +++ b/test/services/postcard_service_test.mocks.dart @@ -296,6 +296,15 @@ class MockPostcardApi extends _i1.Mock implements _i4.PostcardApi { returnValue: _i15.Future.value(), ) as _i15.Future); @override + _i15.Future getMerchandiseEnable(String? tokenId) => + (super.noSuchMethod( + Invocation.method( + #getMerchandiseEnable, + [tokenId], + ), + returnValue: _i15.Future.value(''), + ) as _i15.Future); + @override _i15.Future updatePostcard({ required String? tokenId, required _i16.File? data,