diff --git a/lib/screen/app_router.dart b/lib/screen/app_router.dart index 598c4ce21..8b16424af 100644 --- a/lib/screen/app_router.dart +++ b/lib/screen/app_router.dart @@ -82,6 +82,7 @@ import 'package:autonomy_flutter/screen/irl_screen/sign_message_screen.dart'; import 'package:autonomy_flutter/screen/irl_screen/webview_irl_screen.dart'; import 'package:autonomy_flutter/screen/migration/key_sync_bloc.dart'; import 'package:autonomy_flutter/screen/migration/key_sync_page.dart'; +import 'package:autonomy_flutter/screen/moma_postcard_page/moma_postcard_page.dart'; import 'package:autonomy_flutter/screen/notification_onboarding_page.dart'; import 'package:autonomy_flutter/screen/onboarding/import_address/import_seeds.dart'; import 'package:autonomy_flutter/screen/onboarding/import_address/name_address_persona.dart'; @@ -218,6 +219,7 @@ class AppRouter { static const addToCollectionPage = 'add_to_collection_page'; static const exhibitionDetailPage = 'exhibition_detail_page'; static const feralFileSeriesPage = 'feral_file_series_page'; + static const momaPostcardPage = 'moma_postcard_page'; static Route onGenerateRoute(RouteSettings settings) { final ethereumBloc = EthereumBloc(injector(), injector()); @@ -819,6 +821,10 @@ class AppRouter { ], child: const HiddenArtworksPage(), )); + case momaPostcardPage: + return CupertinoPageRoute( + settings: settings, builder: (context) => const MoMAPostcardPage()); + case exhibitionDetailPage: return CupertinoPageRoute( settings: settings, diff --git a/lib/screen/home/home_navigation_page.dart b/lib/screen/home/home_navigation_page.dart index 7ac104a11..5e90d10b5 100644 --- a/lib/screen/home/home_navigation_page.dart +++ b/lib/screen/home/home_navigation_page.dart @@ -126,7 +126,10 @@ class _HomeNavigationPageState extends State colorFilter: const ColorFilter.mode( AppColor.white, BlendMode.srcIn), // white ), - onTap: () {}, + onTap: () { + Navigator.of(context) + .pushReplacementNamed(AppRouter.momaPostcardPage); + }, ), OptionItem( title: 'wallet'.tr(), diff --git a/lib/screen/moma_postcard_page/moma_postcard_page.dart b/lib/screen/moma_postcard_page/moma_postcard_page.dart new file mode 100644 index 000000000..394454be1 --- /dev/null +++ b/lib/screen/moma_postcard_page/moma_postcard_page.dart @@ -0,0 +1,197 @@ +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// Copyright © 2022 Bitmark. All rights reserved. +// Use of this source code is governed by the BSD-2-Clause Plus Patent License +// that can be found in the LICENSE file. +// + +import 'dart:async'; + +import 'package:autonomy_flutter/common/injector.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_page.dart'; +import 'package:autonomy_flutter/service/client_token_service.dart'; +import 'package:autonomy_flutter/service/metric_client_service.dart'; +import 'package:autonomy_flutter/util/asset_token_ext.dart'; +import 'package:autonomy_flutter/util/constants.dart'; +import 'package:autonomy_flutter/util/style.dart'; +import 'package:autonomy_flutter/util/token_ext.dart'; +import 'package:autonomy_flutter/view/artwork_common_widget.dart'; +import 'package:autonomy_flutter/view/back_appbar.dart'; +import 'package:autonomy_flutter/view/responsive.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:nft_collection/models/asset_token.dart'; +import 'package:nft_collection/nft_collection.dart'; + +class MoMAPostcardPage extends StatefulWidget { + const MoMAPostcardPage({super.key}); + + @override + State createState() => _MoMAPostcardPageState(); +} + +class _MoMAPostcardPageState extends State { + int _cachedImageSize = 0; + final nftBloc = injector().nftBloc; + + @override + void initState() { + super.initState(); + } + + List _updateTokens(List tokens) { + List filteredTokens = tokens.filterAssetToken(); + final filteredPostcards = + filteredTokens.where((element) => element.isPostcard).toList(); + + final nextKey = nftBloc.state.nextKey; + if (nextKey != null && + !nextKey.isLoaded && + filteredPostcards.length < COLLECTION_INITIAL_MIN_SIZE) { + nftBloc.add(GetTokensByOwnerEvent(pageKey: nextKey)); + } + return filteredPostcards; + } + + @override + Widget build(BuildContext context) { + final contentWidget = + BlocBuilder( + bloc: nftBloc, + builder: (context, state) => NftCollectionGrid( + state: state.state, + tokens: _updateTokens(state.tokens.items), + loadingIndicatorBuilder: _loadingView, + emptyGalleryViewBuilder: _emptyHiddenArtwork, + customGalleryViewBuilder: (context, tokens) => + _assetsWidget(context, tokens), + ), + ); + return SafeArea( + child: Scaffold( + appBar: getBackAppBar( + context, + title: 'moma_postcard'.tr(), + onBack: () { + Navigator.of(context).pop(); + }, + ), + body: Column( + children: [ + Expanded( + child: contentWidget, + ), + ], + ), + ), + ); + } + + Widget _loadingView(BuildContext context) => Center( + child: Column( + children: [ + // _header(context), + loadingIndicator(), + ], + )); + + Widget _emptyHiddenArtwork(BuildContext context) => Padding( + padding: ResponsiveLayout.pageEdgeInsets.copyWith(top: 0, bottom: 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + addTitleSpace(), + ], + ), + ); + + Widget _assetsWidget(BuildContext context, List tokens) { + final accountIdentities = tokens + .where((e) => e.pending != true || e.hasMetadata) + .map((element) => element.identity) + .toList(); + + const int cellPerRowPhone = 3; + const int cellPerRowTablet = 6; + const double cellSpacing = 3; + int cellPerRow = + ResponsiveLayout.isMobile ? cellPerRowPhone : cellPerRowTablet; + if (_cachedImageSize == 0) { + final estimatedCellWidth = + MediaQuery.of(context).size.width / cellPerRow - + cellSpacing * (cellPerRow - 1); + _cachedImageSize = (estimatedCellWidth * 3).ceil(); + } + List sources; + sources = [ + SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: cellPerRow, + crossAxisSpacing: cellSpacing, + mainAxisSpacing: cellSpacing, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + final asset = tokens[index]; + + if (asset.pending == true && asset.isPostcard) { + return MintTokenWidget( + thumbnail: asset.galleryThumbnailURL, + tokenId: asset.tokenId, + ); + } + + return GestureDetector( + child: asset.pending == true && !asset.hasMetadata + ? PendingTokenWidget( + thumbnail: asset.galleryThumbnailURL, + tokenId: asset.tokenId, + ) + : tokenGalleryThumbnailWidget( + context, + asset, + _cachedImageSize, + usingThumbnailID: index > 50, + ), + onTap: () { + if (asset.pending == true && !asset.hasMetadata) { + return; + } + + final index = tokens + .where((e) => e.pending != true || e.hasMetadata) + .toList() + .indexOf(asset); + final payload = asset.isPostcard + ? PostcardDetailPagePayload(accountIdentities, index) + : ArtworkDetailPayload(accountIdentities, index); + + final pageName = asset.isPostcard + ? AppRouter.claimedPostcardDetailsPage + : AppRouter.artworkDetailsPage; + unawaited(Navigator.of(context) + .pushNamed(pageName, ////need change to pageName + arguments: payload)); + + unawaited(injector().addEvent( + MixpanelEvent.viewArtwork, + data: {'id': asset.id})); + }, + ); + }, + childCount: tokens.length, + ), + ), + const SliverToBoxAdapter(child: SizedBox(height: 30)), + ]; + + return CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: sources, + controller: ScrollController(), + ); + } +} diff --git a/lib/screen/scan_qr/scan_qr_page.dart b/lib/screen/scan_qr/scan_qr_page.dart index ded5973af..793f13ae4 100644 --- a/lib/screen/scan_qr/scan_qr_page.dart +++ b/lib/screen/scan_qr/scan_qr_page.dart @@ -71,9 +71,12 @@ class _ScanQRPageState extends State late Lock _lock; Timer? _timer; + late bool _shouldPop; + @override void initState() { super.initState(); + _shouldPop = !(widget.scannerItem == ScannerItem.GLOBAL); //There is a conflict with lib qr_code_scanner on Android. if (Platform.isIOS) { unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack)); @@ -250,7 +253,9 @@ class _ScanQRPageState extends State ) : GestureDetector( onTap: () { - Navigator.pop(context); + if (_shouldPop) { + Navigator.pop(context); + } }, child: closeIcon(color: AppColor.white), )), @@ -475,7 +480,9 @@ class _ScanQRPageState extends State if (!mounted) { return; } - Navigator.pop(context); + if (_shouldPop) { + Navigator.pop(context); + } injector().handleDeeplink( code, @@ -510,7 +517,9 @@ class _ScanQRPageState extends State if (!mounted) { return; } - Navigator.pop(context, code); + if (_shouldPop) { + Navigator.pop(context, code); + } break; case ScannerItem.GLOBAL: if (code.startsWith('wc:')) { @@ -577,11 +586,15 @@ class _ScanQRPageState extends State if (!mounted) { return false; } - Navigator.pop(context, device); + if (_shouldPop) { + Navigator.pop(context, device); + } return result; } catch (e) { if (mounted) { - Navigator.pop(context); + if (_shouldPop) { + Navigator.pop(context); + } if (e.toString().contains('DEADLINE_EXCEEDED') || true) { await UIHelper.showInfoDialog( _navigationService.navigatorKey.currentContext!, @@ -638,7 +651,9 @@ class _ScanQRPageState extends State if (!mounted) { return; } - Navigator.pop(context); + if (_shouldPop) { + Navigator.pop(context); + } await Future.delayed(const Duration(seconds: 1)); _addScanQREvent( @@ -654,7 +669,9 @@ class _ScanQRPageState extends State if (!mounted) { return; } - Navigator.pop(context); + if (_shouldPop) { + Navigator.pop(context); + } await Future.delayed(const Duration(seconds: 1)); _addScanQREvent( @@ -668,6 +685,9 @@ class _ScanQRPageState extends State @override void didPopNext() { super.didPopNext(); + setState(() { + _isLoading = false; + }); if (Platform.isIOS) { unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack)); } diff --git a/lib/view/search_bar.dart b/lib/view/search_bar.dart index 50e83e4b0..88389b878 100644 --- a/lib/view/search_bar.dart +++ b/lib/view/search_bar.dart @@ -26,6 +26,7 @@ class _SearchBarState extends State { @override void initState() { super.initState(); + _focusNode.requestFocus(); } @override @@ -104,7 +105,7 @@ class _SearchBarState extends State { }, child: const Icon( AuIcon.close, - size: 12, + size: 14, color: AppColor.primaryBlack, ), ),