From 5db153d64aa00d3e23358f0fe91a2cce1244e29b Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Mon, 23 Sep 2024 11:58:58 +0200 Subject: [PATCH 01/66] created buttons and logic --- .../example/base/lib/utils/crypto/eip155.dart | 2 +- .../base/lib/utils/sample_wallets.dart | 6 +- .../example/modal/lib/home_page.dart | 13 +- .../lib/modal/appkit_modal_impl.dart | 24 +- .../lib/modal/assets/dark/apple_logo.svg | 5 + .../lib/modal/assets/dark/discord_logo.svg | 4 + .../lib/modal/assets/dark/facebook_logo.svg | 13 + .../lib/modal/assets/dark/farcaster_logo.svg | 14 ++ .../lib/modal/assets/dark/github_logo.svg | 11 + .../lib/modal/assets/dark/google_logo.svg | 14 ++ .../modal/assets/dark/more_social_icon.svg | 7 + .../lib/modal/assets/dark/telegram_logo.svg | 16 ++ .../lib/modal/assets/dark/twitch_logo.svg | 15 ++ .../lib/modal/assets/dark/x_logo.svg | 12 + .../lib/modal/assets/light/apple_logo.svg | 5 + .../lib/modal/assets/light/discord_logo.svg | 4 + .../lib/modal/assets/light/facebook_logo.svg | 13 + .../lib/modal/assets/light/farcaster_logo.svg | 14 ++ .../lib/modal/assets/light/github_logo.svg | 11 + .../lib/modal/assets/light/google_logo.svg | 14 ++ .../modal/assets/light/more_social_icon.svg | 7 + .../lib/modal/assets/light/telegram_logo.svg | 16 ++ .../lib/modal/assets/light/twitch_logo.svg | 15 ++ .../lib/modal/assets/light/x_logo.svg | 12 + .../lib/modal/constants/key_constants.dart | 1 + .../lib/modal/constants/style_constants.dart | 1 + .../models/public/appkit_modal_models.dart | 1 + .../models/public/appkit_social_options.dart | 9 + .../appkit_modal_main_wallets_page.dart | 90 +------ .../lib/modal/pages/social_login_page.dart | 226 ++++++++++++++++++ .../blockchain_service.dart | 9 +- .../models/wc_sample_wallets.dart | 6 +- .../magic_service/i_magic_service.dart | 4 + .../services/magic_service/magic_service.dart | 58 ++++- .../magic_service/models/frame_message.dart | 30 +++ .../buttons/email_login_input_field.dart | 73 ++++++ .../buttons/social_login_buttons_view.dart | 148 ++++++++++++ .../lists/list_items/wallet_connect_item.dart | 1 + .../widgets/miscellaneous/input_email.dart | 2 + .../widgets/miscellaneous/searchbar.dart | 20 +- packages/reown_appkit/pubspec.yaml | 1 + 41 files changed, 828 insertions(+), 119 deletions(-) create mode 100644 packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/github_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/google_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/more_social_icon.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/dark/x_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/apple_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/discord_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/github_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/google_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/more_social_icon.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg create mode 100644 packages/reown_appkit/lib/modal/assets/light/x_logo.svg create mode 100644 packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart create mode 100644 packages/reown_appkit/lib/modal/pages/social_login_page.dart create mode 100644 packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart create mode 100644 packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart index 0556792..aa8a441 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart @@ -155,7 +155,7 @@ class EIP155 { required String message, }) async { final bytes = utf8.encode(message); - final encoded = bytesToHex(bytes); + final encoded = bytesToHex(bytes, include0x: true); return await appKit.request( topic: topic, diff --git a/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart b/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart index 885e8e5..29c9e71 100644 --- a/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart +++ b/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart @@ -33,8 +33,7 @@ class WCSampleWallets { 'id': '123456789012345678901234567894', 'schema': 'kotlin-web3wallet://wc', 'bundleId': 'com.walletconnect.sample.wallet.internal', - 'universal': - 'https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_internal', + 'universal': 'https://appkit-lab.reown.com/wallet_internal', }, ]; @@ -69,8 +68,7 @@ class WCSampleWallets { 'id': '123456789012345678901234567893', 'schema': 'kotlin-web3wallet://wc', 'bundleId': 'com.walletconnect.sample.wallet', - 'universal': - 'https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_release', + 'universal': 'https://appkit-lab.reown.com/wallet_release', }, ]; diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index a674459..4b33d59 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -58,7 +58,7 @@ class _MyHomePageState extends State { final internal = widget.bundleId.endsWith('.internal'); final debug = widget.bundleId.endsWith('.debug'); if (internal || debug || kDebugMode) { - return 'internal'; + return '-internal'; } return ''; } @@ -88,7 +88,7 @@ class _MyHomePageState extends State { description: StringConstants.pageTitle, url: _universalLink(), icons: [ - 'https://docs.walletconnect.com/assets/images/web3modalLogo-2cee77e07851ba0a710b56d03d4d09dd.png' + 'https://raw.githubusercontent.com/reown-com/reown_flutter/refs/heads/develop/assets/appkit-icon$_flavor.png', ], redirect: _constructRedirect(), ); @@ -219,6 +219,15 @@ class _MyHomePageState extends State { siweConfig: _siweConfig(siweAuthValue), enableAnalytics: analyticsValue, // OPTIONAL - null by default enableEmail: emailWalletValue, // OPTIONAL - false by default + socials: [ + AppKitSocialOption.X, + AppKitSocialOption.Apple, + AppKitSocialOption.Discord, + AppKitSocialOption.Facebook, + AppKitSocialOption.Farcaster, + AppKitSocialOption.Github, + // AppKitSocialOption.Google, + ], // requiredNamespaces: {}, // optionalNamespaces: {}, // includedWalletIds: {}, diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 32b6fbb..30a5a79 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -141,6 +141,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { Set? excludedWalletIds, bool? enableAnalytics, bool enableEmail = false, + List socials = const [], List blockchains = const [], LogLevel logLevel = LogLevel.nothing, }) { @@ -201,6 +202,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { core: _appKit.core, metadata: _appKit.metadata, enabled: enableEmail, + socials: socials, ); coinbaseService.instance = CoinbaseService( @@ -1335,15 +1337,19 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } // Get the chain balance. - _chainBalance = await blockchainService.instance.rpcRequest( - chainId: '${CoreConstants.namespace}:$_currentSelectedChainId', - request: SessionRequestParams( - method: 'eth_getBalance', - params: [_currentSession!.address!, 'latest'], - ), - ); - final tokenName = selectedChain?.currency ?? ''; - balanceNotifier.value = '$_chainBalance $tokenName'; + try { + _chainBalance = await blockchainService.instance.rpcRequest( + chainId: '${CoreConstants.namespace}:$_currentSelectedChainId', + request: SessionRequestParams( + method: 'eth_getBalance', + params: [_currentSession!.address!, 'latest'], + ), + ); + final tokenName = selectedChain?.currency ?? ''; + balanceNotifier.value = '$_chainBalance $tokenName'; + } catch (e) { + _logger.e('[$runtimeType] loadAccountData $e'); + } // Get the avatar, each chainId is just a number in string form. try { diff --git a/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg new file mode 100644 index 0000000..8a3812b --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg new file mode 100644 index 0000000..5b14521 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg new file mode 100644 index 0000000..a25be48 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg new file mode 100644 index 0000000..4a08fa6 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg new file mode 100644 index 0000000..b4a0d63 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/google_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/google_logo.svg new file mode 100644 index 0000000..eb1c67c --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/google_logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/more_social_icon.svg b/packages/reown_appkit/lib/modal/assets/dark/more_social_icon.svg new file mode 100644 index 0000000..f4f3afd --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/more_social_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg new file mode 100644 index 0000000..1ba0838 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg new file mode 100644 index 0000000..3bf4f4d --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg new file mode 100644 index 0000000..aa77656 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg b/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg new file mode 100644 index 0000000..105244f --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg b/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg new file mode 100644 index 0000000..9a7a57d --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg b/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg new file mode 100644 index 0000000..d265f0b --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg b/packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg new file mode 100644 index 0000000..28c641e --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/github_logo.svg b/packages/reown_appkit/lib/modal/assets/light/github_logo.svg new file mode 100644 index 0000000..ebaa6f4 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/github_logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/google_logo.svg b/packages/reown_appkit/lib/modal/assets/light/google_logo.svg new file mode 100644 index 0000000..49d04c1 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/google_logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/more_social_icon.svg b/packages/reown_appkit/lib/modal/assets/light/more_social_icon.svg new file mode 100644 index 0000000..8411858 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/more_social_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg b/packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg new file mode 100644 index 0000000..8203543 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg b/packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg new file mode 100644 index 0000000..ee227b1 --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/assets/light/x_logo.svg b/packages/reown_appkit/lib/modal/assets/light/x_logo.svg new file mode 100644 index 0000000..54da34d --- /dev/null +++ b/packages/reown_appkit/lib/modal/assets/light/x_logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/reown_appkit/lib/modal/constants/key_constants.dart b/packages/reown_appkit/lib/modal/constants/key_constants.dart index 1ecbb7b..938410d 100644 --- a/packages/reown_appkit/lib/modal/constants/key_constants.dart +++ b/packages/reown_appkit/lib/modal/constants/key_constants.dart @@ -21,6 +21,7 @@ class KeyConstants { static const Key approveTransactionPage = Key('approveTransactionPage'); static const Key confirmEmailPage = Key('confirmEmailPage'); static const Key approveSiwePageKey = Key('approveSiwePageKey'); + static const Key socialLoginPage = Key('socialLoginPage'); // Buttons static const Key helpButtonKey = Key('helpButtonKey'); diff --git a/packages/reown_appkit/lib/modal/constants/style_constants.dart b/packages/reown_appkit/lib/modal/constants/style_constants.dart index d2adf3b..509c19e 100644 --- a/packages/reown_appkit/lib/modal/constants/style_constants.dart +++ b/packages/reown_appkit/lib/modal/constants/style_constants.dart @@ -7,6 +7,7 @@ const kNavbarHeight = 64.0; const kShortWalletListCount = 4; const kLShortWalletListCount = 3; const kSearchFieldHeight = 40.0; +const kEmailFieldHeight = 56.0; const kPadding16 = 16.0; const kPadding12 = 12.0; diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_modal_models.dart b/packages/reown_appkit/lib/modal/models/public/appkit_modal_models.dart index 8698749..2042462 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_modal_models.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_modal_models.dart @@ -4,3 +4,4 @@ export 'appkit_siwe_config.dart'; export 'appkit_modal_session.dart'; export 'appkit_modal_events.dart'; export 'appkit_modal_exceptions.dart'; +export 'appkit_social_options.dart'; diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart b/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart new file mode 100644 index 0000000..e06dda6 --- /dev/null +++ b/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart @@ -0,0 +1,9 @@ +enum AppKitSocialOption { + Google, + X, + Github, + Discord, + Apple, + Facebook, + Farcaster, +} diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart index 30b6b00..ae8e92d 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart @@ -3,16 +3,14 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:reown_appkit/modal/pages/about_wallets.dart'; -import 'package:reown_appkit/modal/pages/confirm_email_page.dart'; import 'package:reown_appkit/modal/pages/connect_wallet_page.dart'; -import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; import 'package:reown_appkit/modal/services/explorer_service/explorer_service_singleton.dart'; import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; -import 'package:reown_appkit/modal/services/magic_service/models/email_login_step.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; -import 'package:reown_appkit/modal/widgets/miscellaneous/input_email.dart'; +import 'package:reown_appkit/modal/widgets/buttons/email_login_input_field.dart'; +import 'package:reown_appkit/modal/widgets/buttons/social_login_buttons_view.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; @@ -88,7 +86,8 @@ class _AppKitModalMainWalletsPageState } final emailEnabled = magicService.instance.isEnabled.value; if (emailEnabled) { - maxHeight += (kSearchFieldHeight * 2); + // TODO multiply depending on social options + maxHeight += (kEmailFieldHeight * 4); } final itemsToShow = items.getRange(0, itemsCount); return ConstrainedBox( @@ -98,7 +97,14 @@ class _AppKitModalMainWalletsPageState service.selectWallet(data); widgetStack.instance.push(const ConnectWalletPage()); }, - firstItem: _EmailLoginWidget(), + firstItem: Column( + children: [ + EmailLoginInputField(), + SocialLoginButtonsView(), + _LoginDivider(), + const SizedBox.square(dimension: kListViewSeparatorHeight), + ], + ), itemList: itemsToShow.toList(), bottomItems: [ AllWalletsItem( @@ -173,75 +179,3 @@ extension on int { return '${toString().substring(0, toString().length - 1)}0+'; } } - -class _EmailLoginWidget extends StatefulWidget { - @override - State<_EmailLoginWidget> createState() => __EmailLoginWidgetState(); -} - -class __EmailLoginWidgetState extends State<_EmailLoginWidget> { - bool _submitted = false; - @override - void initState() { - super.initState(); - magicService.instance.step.addListener(_stepListener); - } - - void _stepListener() { - debugPrint(magicService.instance.step.value.toString()); - if ((magicService.instance.step.value == EmailLoginStep.verifyDevice || - magicService.instance.step.value == EmailLoginStep.verifyOtp || - magicService.instance.step.value == EmailLoginStep.verifyOtp2) && - _submitted) { - widgetStack.instance.push(ConfirmEmailPage()); - _submitted = false; - } - } - - @override - void dispose() { - magicService.instance.step.removeListener(_stepListener); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: magicService.instance.isEnabled, - builder: (context, emailEnabled, _) { - if (!emailEnabled) { - return const SizedBox.shrink(); - } - return Column( - children: [ - InputEmailWidget( - onFocus: (value) { - if (value) { - analyticsService.instance.sendEvent( - EmailLoginSelected(), - ); - } - }, - onValueChange: (value) { - magicService.instance.setEmail(value); - }, - onSubmitted: (value) { - setState(() => _submitted = true); - final service = ModalProvider.of(context).instance; - final chainId = service.selectedChain?.chainId; - analyticsService.instance.sendEvent(EmailSubmitted()); - magicService.instance.connectEmail( - value: value, - chainId: chainId, - ); - }, - ), - const SizedBox.square(dimension: 4.0), - _LoginDivider(), - const SizedBox.square(dimension: kListViewSeparatorHeight), - ], - ); - }, - ); - } -} diff --git a/packages/reown_appkit/lib/modal/pages/social_login_page.dart b/packages/reown_appkit/lib/modal/pages/social_login_page.dart new file mode 100644 index 0000000..59f6b60 --- /dev/null +++ b/packages/reown_appkit/lib/modal/pages/social_login_page.dart @@ -0,0 +1,226 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:reown_appkit/modal/constants/key_constants.dart'; +import 'package:reown_appkit/modal/constants/string_constants.dart'; + +import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; +import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; +import 'package:reown_appkit/modal/utils/asset_util.dart'; +import 'package:reown_appkit/modal/widgets/miscellaneous/content_loading.dart'; +import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.dart'; +import 'package:reown_appkit/modal/widgets/modal_provider.dart'; +import 'package:reown_appkit/modal/widgets/avatars/loading_border.dart'; +import 'package:reown_appkit/modal/widgets/navigation/navbar.dart'; +import 'package:reown_appkit/reown_appkit.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class SocialLoginPage extends StatefulWidget { + const SocialLoginPage({required this.socialOption}) + : super(key: KeyConstants.socialLoginPage); + final AppKitSocialOption socialOption; + + @override + State createState() => _SocialLoginPageState(); +} + +class _SocialLoginPageState extends State + with WidgetsBindingObserver { + IReownAppKitModal? _service; + ModalError? errorEvent; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance.addPostFrameCallback((_) { + _service = ModalProvider.of(context).instance; + _service?.onModalError.subscribe(_errorListener); + _initSocialLogin(widget.socialOption); + setState(() {}); + }); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + // final isOpen = _service?.isOpen ?? false; + // final isConnected = _service?.isConnected ?? false; + // if (isOpen && isConnected && !siweService.instance!.enabled) { + // Future.delayed(Duration(seconds: 1), () { + // if (!mounted) return; + // _service?.closeModal(); + // }); + // } + } + } + + void _errorListener(ModalError? event) => setState(() => errorEvent = event); + + void _initSocialLogin(AppKitSocialOption option) async { + try { + final appKitModal = ModalProvider.of(context).instance; + final redirectUri = await magicService.instance.getSocialRedirectUri( + provider: option.name.toLowerCase(), + chainId: appKitModal.selectedChain?.chainId, + ); + debugPrint('[$runtimeType] _initSocialLogin uri $redirectUri'); + final result = await Navigator.push( + context, + MaterialPageRoute( + fullscreenDialog: true, + barrierDismissible: true, + builder: (context) => _WebViewLoginWidget(url: redirectUri!), + ), + ); + // await ReownCoreUtils.openURL(uri!); + // await _launchUrlInBottomSheet(uri!); + await _completeSocialLogin(result); + } catch (e) { + debugPrint('[$runtimeType] _initSocialLogin error $e'); + } + } + + Future _completeSocialLogin(String url) async { + try { + debugPrint('[$runtimeType] _completeSocialLogin $url'); + final uri = Uri.parse(url); + final result = await magicService.instance.connectSocial( + uri: '?${uri.query}', + ); + debugPrint('[$runtimeType] _completeSocialLogin result $result'); + if (result == true) { + await magicService.instance.getUser(); + } + } catch (e) { + debugPrint('[$runtimeType] _completeSocialLogin error $e'); + } + } + + @override + void dispose() { + _service?.onModalError.unsubscribe(_errorListener); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_service == null) { + return ContentLoading(); + } + final themeData = ReownAppKitModalTheme.getDataOf(context); + final themeColors = ReownAppKitModalTheme.colorsOf(context); + final isPortrait = ResponsiveData.isPortrait(context); + final maxWidth = isPortrait + ? ResponsiveData.maxWidthOf(context) + : ResponsiveData.maxHeightOf(context) - + kNavbarHeight - + (kPadding16 * 2); + return ModalNavbar( + title: widget.socialOption.name, + onBack: () { + // TODO Social Login cancel Social Login flow + // _service?.selectWallet(null); + // widgetStack.instance.pop(); + }, + body: SingleChildScrollView( + scrollDirection: isPortrait ? Axis.vertical : Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: kPadding16), + child: Flex( + direction: isPortrait ? Axis.vertical : Axis.horizontal, + children: [ + Container( + constraints: BoxConstraints(maxWidth: maxWidth), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox.square(dimension: 20.0), + LoadingBorder( + animate: errorEvent == null, + borderRadius: themeData.radiuses.isSquare() + ? 0 + : themeData.radiuses.radiusM + 4.0, + child: SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${widget.socialOption.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + ), + ), + const SizedBox.square(dimension: 20.0), + Text( + 'Log in with ${widget.socialOption.name}', + textAlign: TextAlign.center, + style: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + ), + const SizedBox.square(dimension: 8.0), + Text( + 'Connect in the provider window', + textAlign: TextAlign.center, + style: themeData.textStyles.small500.copyWith( + color: themeColors.foreground200, + ), + ), + const SizedBox.square(dimension: kPadding16), + ], + ), + ), + if (!isPortrait) const SizedBox.square(dimension: kPadding16), + ], + ), + ), + ); + } +} + +class _WebViewLoginWidget extends StatefulWidget { + const _WebViewLoginWidget({required this.url}); + final String url; + + @override + State<_WebViewLoginWidget> createState() => __WebViewLoginWidgetState(); +} + +class __WebViewLoginWidgetState extends State<_WebViewLoginWidget> { + final _webViewController = WebViewController(); + final _authority = Uri.parse(UrlConstants.secureDashboard).authority; + + @override + void initState() { + super.initState(); + _init(); + } + + Future _init() async { + await _webViewController.setNavigationDelegate( + NavigationDelegate( + onNavigationRequest: (NavigationRequest request) { + final uri = Uri.parse(request.url); + final params = uri.queryParameters; + if (uri.authority == _authority && params.containsKey('state')) { + debugPrint('onNavigationRequest ${request.url}'); + Future.delayed(Duration(milliseconds: 500), () { + Navigator.of(context).pop(request.url); + }); + } + return NavigationDecision.navigate; + }, + ), + ); + await _webViewController.loadRequest(Uri.parse(widget.url)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: WebViewWidget( + controller: _webViewController, + ), + ); + } +} diff --git a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart index 6923eed..f1193c0 100644 --- a/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart +++ b/packages/reown_appkit/lib/modal/services/blockchain_service/blockchain_service.dart @@ -89,6 +89,10 @@ class BlockChainService implements IBlockChainService { final amount = EtherAmount.fromBigInt(EtherUnit.wei, hexToInt(result)); return amount.getValueInUnit(EtherUnit.ether); } catch (e) { + _core.logger.e( + '[$runtimeType] Failed to get parse ${request.toJson()}. ' + 'Response: ${response.body}, Status code: ${response.statusCode}', + ); rethrow; } } else { @@ -97,9 +101,8 @@ class BlockChainService implements IBlockChainService { _retries -= 1; await rpcRequest(chainId: chainId, request: request); } else { - _core.logger.i( - '[$runtimeType] Failed to get request ${request.toJson()}. ' - 'Response: ${response.body}, Status code: ${response.statusCode}', + _core.logger.e( + '[$runtimeType] Failed to get request ${request.toJson()}. Response: ${response.body}, Status code: ${response.statusCode}', ); } } diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart b/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart index d8a8f7a..4c348cd 100644 --- a/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart +++ b/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart @@ -35,8 +35,7 @@ class WCSampleWallets { 'id': '123456789012345678901234567894', 'schema': 'kotlin-web3wallet://wc', 'bundleId': 'com.walletconnect.sample.wallet.internal', - 'universal': - 'https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_internal', + 'universal': 'https://appkit-lab.reown.com/wallet_internal', }, ]; @@ -71,8 +70,7 @@ class WCSampleWallets { 'id': '123456789012345678901234567893', 'schema': 'kotlin-web3wallet://wc', 'bundleId': 'com.walletconnect.sample.wallet', - 'universal': - 'https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_release', + 'universal': 'https://appkit-lab.reown.com/wallet_release', }, ]; diff --git a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart index 9fc5354..36d2f17 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart @@ -3,6 +3,7 @@ import 'package:reown_appkit/reown_appkit.dart'; abstract class IMagicService { ConnectionMetadata get metadata; + List get socials; Future init(); @@ -10,6 +11,9 @@ abstract class IMagicService { void setNewEmail(String value); // ****** W3mFrameProvider public methods ******* // + Future getSocialRedirectUri({required String provider}); + Future connectSocial({required String uri}); + Future connectEmail({required String value}); Future updateEmail({required String value}); Future updateEmailPrimaryOtp({required String otp}); diff --git a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart index 800f397..9b5f328 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart @@ -50,13 +50,12 @@ class MagicService implements IMagicService { name: defaultWalletData.listing.name, description: '', url: defaultWalletData.listing.homepage, - icons: [''], + icons: [''], // TODO set the icon here depending on the login type ), publicKey: '', ); // - // final IReownAppKit _appKit; Timer? _timeOutTimer; String? _connectionChainId; int _onLoadCount = 0; @@ -102,14 +101,19 @@ class MagicService implements IMagicService { late final IReownCore _core; late final PairingMetadata _metadata; + late final List _socialOptions; + + @override + List get socials => _socialOptions; MagicService({ - // required IReownAppKit appKit, required IReownCore core, required PairingMetadata metadata, + required List socials, bool enabled = false, }) : _core = core, - _metadata = metadata { + _metadata = metadata, + _socialOptions = socials { isEnabled.value = enabled; if (isEnabled.value) { _webViewController = WebViewController(); @@ -199,6 +203,32 @@ class MagicService implements IMagicService { // ****** W3mFrameProvider public methods ******* // + Completer _getSocialRedirectUri = Completer(); + @override + Future getSocialRedirectUri({ + required String provider, + String? chainId, + }) async { + if (!isEnabled.value || !isReady.value) return null; + _getSocialRedirectUri = Completer(); + _connectionChainId = chainId ?? _connectionChainId; + final message = GetSocialRedirectUri( + provider: provider.toLowerCase(), + ).toString(); + await _webViewController.runJavaScript('sendMessage($message)'); + return await _getSocialRedirectUri.future; + } + + Completer _connectSocial = Completer(); + @override + Future connectSocial({required String uri}) async { + if (!isEnabled.value || !isReady.value) return null; + _connectSocial = Completer(); + final message = ConnectSocial(uri: uri).toString(); + await _webViewController.runJavaScript('sendMessage($message)'); + return await _connectSocial.future; + } + @override Future connectEmail({required String value, String? chainId}) async { if (!isEnabled.value || !isReady.value) return; @@ -270,7 +300,7 @@ class MagicService implements IMagicService { } Future _getUser(String? chainId) async { - final message = GetUser(chainId: chainId).toString(); + final message = GetUser(chainId: chainId ?? _connectionChainId).toString(); return await _webViewController.runJavaScript('sendMessage($message)'); } @@ -378,6 +408,21 @@ class MagicService implements IMagicService { await _getUser(_connectionChainId); } } + if (messageData.getSocialRedirectUriSuccess) { + final uri = messageData.getPayloadMapKey('uri'); + _getSocialRedirectUri.complete(uri); + } + if (messageData.getSocialRedirectUriError) { + // TODO Social Login capture and pass error + _getSocialRedirectUri.complete(null); + } + if (messageData.connectSocialSuccess) { + _connectSocial.complete(true); + } + if (messageData.connectSocialError) { + // TODO Social Login capture and pass error + _connectSocial.complete(false); + } // ****** CONNECT_EMAIL if (messageData.connectEmailSuccess) { if (step.value != EmailLoginStep.loading) { @@ -582,9 +627,6 @@ class MagicService implements IMagicService { '''); } - // sendMessage({type:"@w3m-app/GET_SOCIAL_REDIRECT_URI",payload:{provider:"x"}}); - // sendMessage({type:"@w3m-app/CONNECT_SOCIAL",payload:{uri:"https://auth.magic.link/v1/oauth2/twitter/start?magic_api_key=pk_live_B080E9DC31E5875E&magic_challenge=9HbSG6KYL3r2b7LqzhD7-EcjoHLj-a7wt7npmSBR2fw&state=FfR0W7idPzp81HM2KE~zBPR7bbSQM97CL5zZZMHd_2_ZHZ~rLvnO63MO3fd6eB4LMymif9pQupdhVL11l4NsQk4D-zQDfPGB17PpiWjPobCemCZwP.HdkH4dQeSDgkiH&platform=web&redirect_uri=https%3A%2F%2Fsecure.walletconnect.com%2Fsdk%2Foauth%3FprojectId%3Dcad4956f31a5e40a00b62865b030c6f8"}}); - void _onDebugConsoleReceived(JavaScriptConsoleMessage message) { _core.logger.d('[$runtimeType] JS Console ${message.message}'); } diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart index c64ef98..f056d85 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart @@ -80,6 +80,15 @@ class MessageData { // @w3m-frame events bool get syncThemeSuccess => type == '@w3m-frame/SYNC_THEME_SUCCESS'; bool get syncDataSuccess => type == '@w3m-frame/SYNC_DAPP_DATA_SUCCESS'; + + bool get getSocialRedirectUriSuccess => + type == '@w3m-frame/GET_SOCIAL_REDIRECT_URI_SUCCESS'; + bool get getSocialRedirectUriError => + type == '@w3m-frame/GET_SOCIAL_REDIRECT_URI_ERROR'; + + bool get connectSocialSuccess => type == '@w3m-frame/CONNECT_SOCIAL_SUCCESS'; + bool get connectSocialError => type == '@w3m-frame/CONNECT_SOCIAL_ERROR'; + bool get connectEmailSuccess => type == '@w3m-frame/CONNECT_EMAIL_SUCCESS'; bool get connectEmailError => type == '@w3m-frame/CONNECT_EMAIL_ERROR'; bool get updateEmailSuccess => type == '@w3m-frame/UPDATE_EMAIL_SUCCESS'; @@ -127,6 +136,27 @@ class SwitchNetwork extends MessageData { String toString() => '{type:\'${super.type}\',payload:{chainId:$chainId}}'; } +class GetSocialRedirectUri extends MessageData { + final String provider; + GetSocialRedirectUri({ + required this.provider, + }) : super(type: '@w3m-app/GET_SOCIAL_REDIRECT_URI'); + + @override + String toString() => + '{type:\'${super.type}\',payload:{provider:\'$provider\'}}'; +} + +class ConnectSocial extends MessageData { + final String uri; + ConnectSocial({ + required this.uri, + }) : super(type: '@w3m-app/CONNECT_SOCIAL'); + + @override + String toString() => '{type:\'${super.type}\',payload:{uri:\'$uri\'}}'; +} + class ConnectEmail extends MessageData { final String email; ConnectEmail({required this.email}) : super(type: '@w3m-app/CONNECT_EMAIL'); diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart b/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart new file mode 100644 index 0000000..85d0396 --- /dev/null +++ b/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:reown_appkit/modal/pages/confirm_email_page.dart'; +import 'package:reown_appkit/modal/services/analytics_service/analytics_service_singleton.dart'; +import 'package:reown_appkit/modal/services/analytics_service/models/analytics_event.dart'; +import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; +import 'package:reown_appkit/modal/services/magic_service/models/email_login_step.dart'; +import 'package:reown_appkit/modal/widgets/miscellaneous/input_email.dart'; +import 'package:reown_appkit/modal/widgets/modal_provider.dart'; +import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; + +class EmailLoginInputField extends StatefulWidget { + const EmailLoginInputField({super.key}); + + @override + State createState() => _EmailLoginInputFieldState(); +} + +class _EmailLoginInputFieldState extends State { + bool _submitted = false; + @override + void initState() { + super.initState(); + magicService.instance.step.addListener(_stepListener); + } + + void _stepListener() { + if ((magicService.instance.step.value == EmailLoginStep.verifyDevice || + magicService.instance.step.value == EmailLoginStep.verifyOtp || + magicService.instance.step.value == EmailLoginStep.verifyOtp2) && + _submitted) { + widgetStack.instance.push(ConfirmEmailPage()); + _submitted = false; + } + } + + @override + void dispose() { + magicService.instance.step.removeListener(_stepListener); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: magicService.instance.isEnabled, + builder: (context, emailEnabled, _) { + if (!emailEnabled) { + return const SizedBox.shrink(); + } + return InputEmailWidget( + onFocus: (value) { + if (value) { + analyticsService.instance.sendEvent(EmailLoginSelected()); + } + }, + onValueChange: (value) { + magicService.instance.setEmail(value); + }, + onSubmitted: (value) { + setState(() => _submitted = true); + final service = ModalProvider.of(context).instance; + final chainId = service.selectedChain?.chainId; + analyticsService.instance.sendEvent(EmailSubmitted()); + magicService.instance.connectEmail( + value: value, + chainId: chainId, + ); + }, + ); + }, + ); + } +} diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart b/packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart new file mode 100644 index 0000000..8d566ef --- /dev/null +++ b/packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart @@ -0,0 +1,148 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/pages/social_login_page.dart'; +import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; +import 'package:reown_appkit/modal/utils/asset_util.dart'; +import 'package:reown_appkit/modal/widgets/lists/list_items/base_list_item.dart'; +import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; +import 'package:reown_appkit/reown_appkit.dart'; + +class SocialLoginButtonsView extends StatefulWidget { + const SocialLoginButtonsView({super.key}); + + @override + State createState() => _SocialLoginButtonsViewState(); +} + +class _SocialLoginButtonsViewState extends State { + @override + Widget build(BuildContext context) { + final themeData = ReownAppKitModalTheme.getDataOf(context); + final themeColors = ReownAppKitModalTheme.colorsOf(context); + return ValueListenableBuilder( + valueListenable: magicService.instance.isReady, + builder: (context, value, _) { + final socialButtons = magicService.instance.socials; + final count = socialButtons.length; + if (count == 0) { + return SizedBox.shrink(); + } + final fits = count == 6; + final exceeds = count > 6; + // + final firstItem = socialButtons.first; + final restItems = fits + ? socialButtons.sublist(1, min(socialButtons.length, 6)) + : socialButtons.sublist(1, min(socialButtons.length, 5)); + + final secondRowList = [ + ...restItems.map( + (item) => Expanded( + child: BaseListItem( + onTap: value ? () => _initSocialLogin(item) : null, + child: LayoutBuilder( + builder: (_, constraints) { + return SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${item.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + height: constraints.maxHeight, + width: constraints.maxHeight, + ); + }, + ), + ), + ), + ), + if (exceeds) + Expanded( + child: BaseListItem( + // TODO Social Login + // onTap: value ? () => _initSocialLogin() : null, + child: LayoutBuilder( + builder: (_, constraints) { + return SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + 'more_social_icon.svg', + ), + package: 'reown_appkit', + height: constraints.maxHeight, + width: constraints.maxHeight, + ); + }, + ), + ), + ), + ]; + return Column( + children: [ + const SizedBox.square(dimension: kListViewSeparatorHeight), + BaseListItem( + onTap: value ? () => _initSocialLogin(firstItem) : null, + child: Row( + children: [ + LayoutBuilder( + builder: (_, constraints) { + return SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${firstItem.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + height: constraints.maxHeight, + width: constraints.maxHeight, + ); + }, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Text( + 'Continue with ${firstItem.name}', + style: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + ), + ), + ], + ), + ), + Column( + children: [ + const SizedBox.square(dimension: kListViewSeparatorHeight), + Row( + children: _buttonsWithDivider(secondRowList), + ), + ], + ), + const SizedBox.square(dimension: kListViewSeparatorHeight), + ], + ); + }, + ); + } + + void _initSocialLogin(AppKitSocialOption option) => widgetStack.instance.push( + SocialLoginPage( + socialOption: option, + ), + ); + + List _buttonsWithDivider(List widgets) { + List spacedWidgets = []; + for (int i = 0; i < widgets.length; i++) { + spacedWidgets.add(widgets[i]); + if (i < widgets.length - 1) { + spacedWidgets.add( + const SizedBox.square(dimension: kListViewSeparatorHeight), + ); + } + } + return spacedWidgets; + } +} diff --git a/packages/reown_appkit/lib/modal/widgets/lists/list_items/wallet_connect_item.dart b/packages/reown_appkit/lib/modal/widgets/lists/list_items/wallet_connect_item.dart index 370ec6b..57dad46 100644 --- a/packages/reown_appkit/lib/modal/widgets/lists/list_items/wallet_connect_item.dart +++ b/packages/reown_appkit/lib/modal/widgets/lists/list_items/wallet_connect_item.dart @@ -1,3 +1,4 @@ +// TODO DELETE THIS FILE import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart index e01cbe2..1e246f9 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; import 'package:reown_appkit/modal/utils/core_utils.dart'; import 'package:reown_appkit/modal/widgets/circular_loader.dart'; @@ -66,6 +67,7 @@ class _InputEmailWidgetState extends State { Widget build(BuildContext context) { final themeColors = ReownAppKitModalTheme.colorsOf(context); return ModalSearchBar( + height: kEmailFieldHeight, enabled: !_timedOut && _ready && !_submitted, controller: _controller, initialValue: _controller.text, diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart index 1c1d622..d9604cd 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart @@ -29,6 +29,7 @@ class ModalSearchBar extends StatefulWidget { this.debounce = true, this.focusNode, this.width, + this.height = kSearchFieldHeight, this.enabled = true, this.inputFormatters, }); @@ -52,6 +53,7 @@ class ModalSearchBar extends StatefulWidget { final bool debounce; final FocusNode? focusNode; final double? width; + final double height; final bool enabled; final List? inputFormatters; @@ -110,10 +112,9 @@ class _ModalSearchBarState extends State void _setDecoration() { final themeColors = ReownAppKitModalTheme.colorsOf(context); - final radiuses = ReownAppKitModalTheme.radiusesOf(context); _decorationTween = DecorationTween( begin: BoxDecoration( - borderRadius: BorderRadius.circular(radiuses.radiusXS), + borderRadius: BorderRadius.circular(widget.height * 0.4), boxShadow: [ BoxShadow( color: Colors.transparent, @@ -125,7 +126,7 @@ class _ModalSearchBarState extends State ], ), end: BoxDecoration( - borderRadius: BorderRadius.circular(radiuses.radiusXS), + borderRadius: BorderRadius.circular(widget.height * 0.4), boxShadow: [ BoxShadow( color: themeColors.accenGlass015, @@ -172,10 +173,9 @@ class _ModalSearchBarState extends State Widget build(BuildContext context) { final themeData = ReownAppKitModalTheme.getDataOf(context); final themeColors = ReownAppKitModalTheme.colorsOf(context); - final radiuses = ReownAppKitModalTheme.radiusesOf(context); final unfocusedBorder = OutlineInputBorder( borderSide: BorderSide(color: themeColors.grayGlass005, width: 1.0), - borderRadius: BorderRadius.circular(radiuses.radius2XS), + borderRadius: BorderRadius.circular(widget.height * 0.3), ); final focusedBorder = unfocusedBorder.copyWith( borderSide: BorderSide(color: themeColors.accent100, width: 1.0), @@ -187,7 +187,7 @@ class _ModalSearchBarState extends State return DecoratedBoxTransition( decoration: _decorationTween.animate(_animationController), child: Container( - height: kSearchFieldHeight + 8.0, + height: widget.height + 8.0, width: widget.width, padding: const EdgeInsets.all(4.0), child: TextFormField( @@ -249,8 +249,8 @@ class _ModalSearchBarState extends State ], ), prefixIconConstraints: BoxConstraints( - maxHeight: kSearchFieldHeight, - minHeight: kSearchFieldHeight, + maxHeight: widget.height, + minHeight: widget.height, maxWidth: 36.0, minWidth: widget.noIcons ? 0.0 : 36.0, ), @@ -292,8 +292,8 @@ class _ModalSearchBarState extends State ) : null), suffixIconConstraints: BoxConstraints( - maxHeight: kSearchFieldHeight, - minHeight: kSearchFieldHeight, + maxHeight: widget.height, + minHeight: widget.height, maxWidth: 36.0, minWidth: widget.noIcons ? 0.0 : 36.0, ), diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index 830494f..1b4bdcd 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: event: ^2.1.2 flutter: sdk: flutter + # flutter_custom_tabs: ^2.1.0 flutter_svg: ^2.0.10+1 freezed_annotation: ^2.4.1 http: ^1.1.2 From 6aae11ad2d0d5b259ee8e3d6ecd0dfa89646af1e Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Mon, 23 Sep 2024 18:16:21 +0200 Subject: [PATCH 02/66] Minor changes --- .../example/modal/lib/home_page.dart | 10 +++-- .../lib/modal/assets/dark/apple_logo.svg | 23 ++++++++--- .../lib/modal/assets/dark/discord_logo.svg | 21 ++++++++-- .../lib/modal/assets/dark/facebook_logo.svg | 39 ++++++++++++------- .../lib/modal/assets/dark/farcaster_logo.svg | 6 +-- .../lib/modal/assets/dark/github_logo.svg | 29 ++++++++------ .../lib/modal/assets/dark/google_logo.svg | 32 ++++++++------- .../lib/modal/assets/dark/telegram_logo.svg | 32 +++++++-------- .../lib/modal/assets/dark/twitch_logo.svg | 33 +++++++++------- .../lib/modal/assets/dark/x_logo.svg | 24 ++++++------ .../lib/modal/assets/light/apple_logo.svg | 23 ++++++++--- .../lib/modal/assets/light/discord_logo.svg | 21 ++++++++-- .../lib/modal/assets/light/facebook_logo.svg | 39 ++++++++++++------- .../lib/modal/assets/light/github_logo.svg | 29 ++++++++------ .../lib/modal/assets/light/google_logo.svg | 32 ++++++++------- .../lib/modal/assets/light/telegram_logo.svg | 32 +++++++-------- .../lib/modal/assets/light/twitch_logo.svg | 33 +++++++++------- .../lib/modal/assets/light/x_logo.svg | 24 ++++++------ .../lib/modal/constants/key_constants.dart | 1 + .../models/public/appkit_social_options.dart | 2 + .../lists/list_items/all_wallets_item.dart | 2 +- .../lists/list_items/base_list_item.dart | 7 +++- .../widgets/miscellaneous/input_email.dart | 2 +- .../widgets/miscellaneous/searchbar.dart | 8 ++-- 24 files changed, 310 insertions(+), 194 deletions(-) diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 4b33d59..2c9cad1 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -88,7 +88,7 @@ class _MyHomePageState extends State { description: StringConstants.pageTitle, url: _universalLink(), icons: [ - 'https://raw.githubusercontent.com/reown-com/reown_flutter/refs/heads/develop/assets/appkit-icon$_flavor.png', + 'https://raw.githubusercontent.com/reown-com/reown_flutter/refs/heads/develop/assets/appkit_logo.png', ], redirect: _constructRedirect(), ); @@ -220,13 +220,15 @@ class _MyHomePageState extends State { enableAnalytics: analyticsValue, // OPTIONAL - null by default enableEmail: emailWalletValue, // OPTIONAL - false by default socials: [ - AppKitSocialOption.X, + AppKitSocialOption.Google, AppKitSocialOption.Apple, AppKitSocialOption.Discord, + AppKitSocialOption.Github, + AppKitSocialOption.X, AppKitSocialOption.Facebook, AppKitSocialOption.Farcaster, - AppKitSocialOption.Github, - // AppKitSocialOption.Google, + AppKitSocialOption.Twitch, + AppKitSocialOption.Telegram, ], // requiredNamespaces: {}, // optionalNamespaces: {}, diff --git a/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg index 8a3812b..b32ec0d 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg @@ -1,5 +1,18 @@ - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg index 5b14521..c1297ca 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg @@ -1,4 +1,17 @@ - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg index a25be48..36d6d69 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg @@ -1,13 +1,26 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg index 4a08fa6..28c641e 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg @@ -1,13 +1,13 @@ - + - + - + diff --git a/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg index b4a0d63..e8f413a 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg @@ -1,11 +1,18 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/google_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/google_logo.svg index eb1c67c..f2607cd 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/google_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/google_logo.svg @@ -1,14 +1,18 @@ - - - - - - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg index 1ba0838..c694fcd 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/telegram_logo.svg @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg index 3bf4f4d..706f022 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/twitch_logo.svg @@ -1,15 +1,18 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg index aa77656..8fbeb50 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg @@ -1,12 +1,12 @@ - - - - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg b/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg index 105244f..b32ec0d 100644 --- a/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg @@ -1,5 +1,18 @@ - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg b/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg index 9a7a57d..c1297ca 100644 --- a/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg @@ -1,4 +1,17 @@ - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg b/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg index d265f0b..36d6d69 100644 --- a/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg @@ -1,13 +1,26 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/github_logo.svg b/packages/reown_appkit/lib/modal/assets/light/github_logo.svg index ebaa6f4..e8f413a 100644 --- a/packages/reown_appkit/lib/modal/assets/light/github_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/github_logo.svg @@ -1,11 +1,18 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/google_logo.svg b/packages/reown_appkit/lib/modal/assets/light/google_logo.svg index 49d04c1..f2607cd 100644 --- a/packages/reown_appkit/lib/modal/assets/light/google_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/google_logo.svg @@ -1,14 +1,18 @@ - - - - - - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg b/packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg index 8203543..c694fcd 100644 --- a/packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/telegram_logo.svg @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg b/packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg index ee227b1..706f022 100644 --- a/packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/twitch_logo.svg @@ -1,15 +1,18 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/x_logo.svg b/packages/reown_appkit/lib/modal/assets/light/x_logo.svg index 54da34d..8fbeb50 100644 --- a/packages/reown_appkit/lib/modal/assets/light/x_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/x_logo.svg @@ -1,12 +1,12 @@ - - - - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/constants/key_constants.dart b/packages/reown_appkit/lib/modal/constants/key_constants.dart index 938410d..bf643e4 100644 --- a/packages/reown_appkit/lib/modal/constants/key_constants.dart +++ b/packages/reown_appkit/lib/modal/constants/key_constants.dart @@ -12,6 +12,7 @@ class KeyConstants { static const Key helpPageKey = Key('helpPageKey'); static const Key qrCodePageKey = Key('qrCodePageKey'); static const Key walletListShortPageKey = Key('walletListShortPageKey'); + static const Key allSocialLoginPageKey = Key('allSocialLoginPageKey'); static const Key walletListLongPageKey = Key('walletListLongPageKey'); static const Key connectWalletPageKey = Key('connectWalletPageKey'); static const Key connecNetworkPageKey = Key('connecNetworkPageKey'); diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart b/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart index e06dda6..2838128 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart @@ -6,4 +6,6 @@ enum AppKitSocialOption { Apple, Facebook, Farcaster, + Twitch, + Telegram, } diff --git a/packages/reown_appkit/lib/modal/widgets/lists/list_items/all_wallets_item.dart b/packages/reown_appkit/lib/modal/widgets/lists/list_items/all_wallets_item.dart index 4abbc06..697f6e6 100644 --- a/packages/reown_appkit/lib/modal/widgets/lists/list_items/all_wallets_item.dart +++ b/packages/reown_appkit/lib/modal/widgets/lists/list_items/all_wallets_item.dart @@ -42,9 +42,9 @@ class AllWalletsItem extends StatelessWidget { ), ), ), - trailing ?? const SizedBox.shrink(), ], ), + trailing: trailing, ); } } diff --git a/packages/reown_appkit/lib/modal/widgets/lists/list_items/base_list_item.dart b/packages/reown_appkit/lib/modal/widgets/lists/list_items/base_list_item.dart index faad699..1e244e8 100644 --- a/packages/reown_appkit/lib/modal/widgets/lists/list_items/base_list_item.dart +++ b/packages/reown_appkit/lib/modal/widgets/lists/list_items/base_list_item.dart @@ -53,7 +53,12 @@ class BaseListItem extends StatelessWidget { ), child: Padding( padding: padding ?? const EdgeInsets.all(8.0), - child: child, + child: Row( + children: [ + Expanded(child: child), + trailing ?? SizedBox.shrink(), + ], + ), ), ); } diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart index 1e246f9..cd31b7e 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart @@ -67,7 +67,7 @@ class _InputEmailWidgetState extends State { Widget build(BuildContext context) { final themeColors = ReownAppKitModalTheme.colorsOf(context); return ModalSearchBar( - height: kEmailFieldHeight, + height: kListItemHeight, enabled: !_timedOut && _ready && !_submitted, controller: _controller, initialValue: _controller.text, diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart index d9604cd..1a41caf 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart @@ -228,7 +228,7 @@ class _ModalSearchBarState extends State Container( width: 16.0, height: 16.0, - margin: const EdgeInsets.only(left: kPadding12), + margin: const EdgeInsets.only(left: kPadding16), child: GestureDetector( onTap: () { _controller.clear(); @@ -251,8 +251,8 @@ class _ModalSearchBarState extends State prefixIconConstraints: BoxConstraints( maxHeight: widget.height, minHeight: widget.height, - maxWidth: 36.0, - minWidth: widget.noIcons ? 0.0 : 36.0, + maxWidth: 40.0, + minWidth: widget.noIcons ? 0.0 : 40.0, ), labelStyle: themeData.textStyles.paragraph500.copyWith( color: themeColors.inverse100, @@ -303,7 +303,7 @@ class _ModalSearchBarState extends State disabledBorder: disabledBorder, focusedBorder: focusedBorder, filled: true, - fillColor: themeColors.grayGlass005, + fillColor: themeColors.grayGlass002, contentPadding: const EdgeInsets.all(0.0), ), ), From 183447eefdef9013bb4b8c0579c655e2b1380c86 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Tue, 24 Sep 2024 15:43:44 +0200 Subject: [PATCH 03/66] Minor changes --- .../base/lib/utils/sample_wallets.dart | 4 +- .../android/app/src/main/AndroidManifest.xml | 4 +- .../lib/modal/constants/style_constants.dart | 1 - .../lib/modal/pages/account_page.dart | 156 +++++++++++------- .../lib/modal/pages/get_wallet_page.dart | 35 ++-- .../models/wc_sample_wallets.dart | 4 +- .../magic_service/models/frame_message.dart | 53 +++--- .../magic_service/models/magic_data.dart | 10 ++ .../lib/modal/widgets/lists/wallets_list.dart | 30 ++-- .../widgets/miscellaneous/input_email.dart | 9 +- 10 files changed, 188 insertions(+), 118 deletions(-) diff --git a/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart b/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart index 29c9e71..24b43c3 100644 --- a/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart +++ b/packages/reown_appkit/example/base/lib/utils/sample_wallets.dart @@ -32,7 +32,7 @@ class WCSampleWallets { 'platform': ['android'], 'id': '123456789012345678901234567894', 'schema': 'kotlin-web3wallet://wc', - 'bundleId': 'com.walletconnect.sample.wallet.internal', + 'bundleId': 'com.reown.sample.wallet.internal', 'universal': 'https://appkit-lab.reown.com/wallet_internal', }, ]; @@ -67,7 +67,7 @@ class WCSampleWallets { 'platform': ['android'], 'id': '123456789012345678901234567893', 'schema': 'kotlin-web3wallet://wc', - 'bundleId': 'com.walletconnect.sample.wallet', + 'bundleId': 'com.reown.sample.wallet', 'universal': 'https://appkit-lab.reown.com/wallet_release', }, ]; diff --git a/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml b/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml index a56e8f2..968b07b 100644 --- a/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml +++ b/packages/reown_appkit/example/modal/android/app/src/main/AndroidManifest.xml @@ -52,8 +52,8 @@ - - + + widgetStack.instance.push(UpgradeWalletPage()), - ), - ], - ), + child: _UpgradeWalletButton(), ), Visibility( visible: isEmailLogin, - child: Column( - children: [ - const SizedBox.square(dimension: kPadding8), - AccountListItem( - iconPath: 'lib/modal/assets/icons/mail.svg', - iconColor: themeColors.foreground100, - title: _service.session?.email ?? '', - titleStyle: themeData.textStyles.paragraph500.copyWith( - color: themeColors.foreground100, - ), - onTap: () { - widgetStack.instance.push(EditEmailPage()); - }, - ), - ], - ), + child: _EmailLoginButton(), ), const SizedBox.square(dimension: kPadding8), _SelectNetworkButton(), @@ -201,6 +155,96 @@ class _DefaultAccountView extends StatelessWidget { } } +class _UpgradeWalletButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + final themeData = ReownAppKitModalTheme.getDataOf(context); + final themeColors = ReownAppKitModalTheme.colorsOf(context); + final radiuses = ReownAppKitModalTheme.radiusesOf(context); + return Column( + children: [ + const SizedBox.square(dimension: kPadding8), + AccountListItem( + padding: const EdgeInsets.symmetric( + horizontal: kPadding8, + vertical: kPadding12, + ), + iconWidget: Padding( + padding: const EdgeInsets.all(4.0), + child: RoundedIcon( + borderRadius: radiuses.isSquare() + ? 0.0 + : radiuses.isCircular() + ? 40.0 + : 8.0, + size: 40.0, + assetPath: 'lib/modal/assets/icons/regular/wallet.svg', + assetColor: themeColors.accent100, + circleColor: themeColors.accenGlass010, + borderColor: themeColors.accenGlass010, + ), + ), + title: 'Upgrade your wallet', + subtitle: 'Transition to a self-custodial wallet', + hightlighted: true, + flexible: true, + titleStyle: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + onTap: () => widgetStack.instance.push(UpgradeWalletPage()), + ), + ], + ); + } +} + +class _EmailLoginButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + final service = ModalProvider.of(context).instance; + final themeData = ReownAppKitModalTheme.getDataOf(context); + final themeColors = ReownAppKitModalTheme.colorsOf(context); + final radiuses = ReownAppKitModalTheme.radiusesOf(context); + final provider = AppKitSocialOption.values.firstWhereOrNull( + (e) => e.name == service.session?.peer?.metadata.name, + ); + return Column( + children: [ + const SizedBox.square(dimension: kPadding8), + AccountListItem( + iconWidget: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: provider == null + ? RoundedIcon( + assetPath: 'lib/modal/assets/icons/mail.svg', + assetColor: themeColors.foreground100, + borderRadius: radiuses.isSquare() ? 0.0 : null, + ) + : SizedBox.square( + dimension: 34.0, + child: SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${provider.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + ), + ), + ), + title: service.session?.email ?? '', + titleStyle: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + onTap: provider == null + ? () => widgetStack.instance.push(EditEmailPage()) + : null, + trailing: provider != null ? const SizedBox.shrink() : null, + ), + ], + ); + } +} + class _SelectNetworkButton extends StatelessWidget { @override Widget build(BuildContext context) { @@ -230,12 +274,10 @@ class _SelectNetworkButton extends StatelessWidget { titleStyle: themeData.textStyles.paragraph500.copyWith( color: themeColors.foreground100, ), - onTap: () { - widgetStack.instance.push( - ReownAppKitModalSelectNetworkPage(), - event: ClickNetworksEvent(), - ); - }, + onTap: () => widgetStack.instance.push( + ReownAppKitModalSelectNetworkPage(), + event: ClickNetworksEvent(), + ), ); } } diff --git a/packages/reown_appkit/lib/modal/pages/get_wallet_page.dart b/packages/reown_appkit/lib/modal/pages/get_wallet_page.dart index 478378a..6c42806 100644 --- a/packages/reown_appkit/lib/modal/pages/get_wallet_page.dart +++ b/packages/reown_appkit/lib/modal/pages/get_wallet_page.dart @@ -60,23 +60,28 @@ class GetWalletPage extends StatelessWidget { } }, bottomItems: [ - AllWalletsItem( - title: 'Explore all', - onTap: () => uriService.instance.launchUrl( - Uri.parse(UrlConstants.exploreWallets), - mode: LaunchMode.externalApplication, + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4.0, ), - trailing: Padding( - padding: const EdgeInsets.only(right: 8.0), - child: SvgPicture.asset( - 'lib/modal/assets/icons/arrow_top_right.svg', - package: 'reown_appkit', - colorFilter: ColorFilter.mode( - themeColors.foreground200, - BlendMode.srcIn, + child: AllWalletsItem( + title: 'Explore all', + onTap: () => uriService.instance.launchUrl( + Uri.parse(UrlConstants.exploreWallets), + mode: LaunchMode.externalApplication, + ), + trailing: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: SvgPicture.asset( + 'lib/modal/assets/icons/arrow_top_right.svg', + package: 'reown_appkit', + colorFilter: ColorFilter.mode( + themeColors.foreground200, + BlendMode.srcIn, + ), + width: 18.0, + height: 18.0, ), - width: 18.0, - height: 18.0, ), ), ), diff --git a/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart b/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart index 4c348cd..1d40759 100644 --- a/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart +++ b/packages/reown_appkit/lib/modal/services/explorer_service/models/wc_sample_wallets.dart @@ -34,7 +34,7 @@ class WCSampleWallets { 'platform': ['android'], 'id': '123456789012345678901234567894', 'schema': 'kotlin-web3wallet://wc', - 'bundleId': 'com.walletconnect.sample.wallet.internal', + 'bundleId': 'com.reown.sample.wallet.internal', 'universal': 'https://appkit-lab.reown.com/wallet_internal', }, ]; @@ -69,7 +69,7 @@ class WCSampleWallets { 'platform': ['android'], 'id': '123456789012345678901234567893', 'schema': 'kotlin-web3wallet://wc', - 'bundleId': 'com.walletconnect.sample.wallet', + 'bundleId': 'com.reown.sample.wallet', 'universal': 'https://appkit-lab.reown.com/wallet_release', }, ]; diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart index f056d85..a611f7f 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart @@ -133,7 +133,7 @@ class SwitchNetwork extends MessageData { }) : super(type: '@w3m-app/SWITCH_NETWORK'); @override - String toString() => '{type:\'${super.type}\',payload:{chainId:$chainId}}'; + String toString() => '{type:"${super.type}",payload:{chainId:$chainId}}'; } class GetSocialRedirectUri extends MessageData { @@ -143,8 +143,7 @@ class GetSocialRedirectUri extends MessageData { }) : super(type: '@w3m-app/GET_SOCIAL_REDIRECT_URI'); @override - String toString() => - '{type:\'${super.type}\',payload:{provider:\'$provider\'}}'; + String toString() => '{type:"${super.type}",payload:{provider:"$provider"}}'; } class ConnectSocial extends MessageData { @@ -154,7 +153,7 @@ class ConnectSocial extends MessageData { }) : super(type: '@w3m-app/CONNECT_SOCIAL'); @override - String toString() => '{type:\'${super.type}\',payload:{uri:\'$uri\'}}'; + String toString() => '{type:"${super.type}",payload:{uri:"$uri"}}'; } class ConnectEmail extends MessageData { @@ -162,7 +161,7 @@ class ConnectEmail extends MessageData { ConnectEmail({required this.email}) : super(type: '@w3m-app/CONNECT_EMAIL'); @override - String toString() => '{type:\'${super.type}\',payload:{email:\'$email\'}}'; + String toString() => '{type:"${super.type}",payload:{email:"$email"}}'; } class UpdateEmail extends MessageData { @@ -170,7 +169,7 @@ class UpdateEmail extends MessageData { UpdateEmail({required this.email}) : super(type: '@w3m-app/UPDATE_EMAIL'); @override - String toString() => '{type:\'${super.type}\',payload:{email:\'$email\'}}'; + String toString() => '{type:"${super.type}",payload:{email:"$email"}}'; } class UpdateEmailPrimaryOtp extends MessageData { @@ -180,7 +179,7 @@ class UpdateEmailPrimaryOtp extends MessageData { }) : super(type: '@w3m-app/UPDATE_EMAIL_PRIMARY_OTP'); @override - String toString() => '{type:\'${super.type}\',payload:{otp:\'$otp\'}}'; + String toString() => '{type:"${super.type}",payload:{otp:"$otp"}}'; } class UpdateEmailSecondaryOtp extends MessageData { @@ -190,7 +189,7 @@ class UpdateEmailSecondaryOtp extends MessageData { }) : super(type: '@w3m-app/UPDATE_EMAIL_SECONDARY_OTP'); @override - String toString() => '{type:\'${super.type}\',payload:{otp:\'$otp\'}}'; + String toString() => '{type:"${super.type}",payload:{otp:"$otp"}}'; } class ConnectOtp extends MessageData { @@ -198,7 +197,7 @@ class ConnectOtp extends MessageData { ConnectOtp({required this.otp}) : super(type: '@w3m-app/CONNECT_OTP'); @override - String toString() => '{type:\'${super.type}\',payload:{otp:\'$otp\'}}'; + String toString() => '{type:"${super.type}",payload:{otp:"$otp"}}'; } class GetUser extends MessageData { @@ -208,9 +207,9 @@ class GetUser extends MessageData { @override String toString() { if ((chainId ?? '').isNotEmpty) { - return '{type:\'${super.type}\',payload:{chainId:$chainId}}'; + return '{type:"${super.type}",payload:{chainId:$chainId}}'; } - return '{type:\'${super.type}\'}'; + return '{type:"${super.type}"}'; } } @@ -240,14 +239,14 @@ class RpcRequest extends MessageData { @override String toString() { debugPrint('[$runtimeType] method $method'); - final m = 'method:\'$method\''; - final t = 'type:\'${super.type}\''; + final m = 'method:"$method"'; + final t = 'type:"${super.type}"'; final p = params.map((i) => '$i').toList(); if (method == 'personal_sign') { final data = p.first; final address = p.last; - return '{$t,payload:{$m,params:[\'$data\',\'$address\']}}'; + return '{$t,payload:{$m,params:["$data","$address"]}}'; } if (method == 'eth_signTypedData_v4' || method == 'eth_signTypedData_v3' || @@ -255,7 +254,7 @@ class RpcRequest extends MessageData { // final data = jsonEncode(jsonDecode(p.first) as Map); final data = p.first; final address = p.last; - return '{$t,payload:{$m,params:[\'$address\',\'$data\']}}'; + return '{$t,payload:{$m,params:["$address","$data"]}}'; } if (method == 'eth_sendTransaction' || method == 'eth_signTransaction') { final jp = jsonEncode(params.first); @@ -282,20 +281,20 @@ class SyncTheme extends MessageData { colors = themeData.lightColors; } - final tm = 'themeMode:\'$mode\''; + final tm = 'themeMode:"$mode"'; final mix = RenderUtils.colorToRGBA(colors.background125); - final tv1 = '\'--w3m-color-mix\':\'$mix\''; - // final tv2 = '\'--w3m-color-mix-strength\':\'0%\''; + final tv1 = '"--w3m-color-mix":"$mix"'; + // final tv2 = '"--w3m-color-mix-strength":"0%"'; final tv = 'themeVariables:{$tv1}'; final accent = RenderUtils.colorToRGBA(colors.accent100); - final wtv1 = '\'--w3m-accent\':\'$accent\''; + final wtv1 = '"--w3m-accent":"$accent"'; final background = RenderUtils.colorToRGBA(colors.background125); - final wtv2 = '\'--w3m-background\':\'$background\''; + final wtv2 = '"--w3m-background":"$background"'; final w3mtv = 'w3mThemeVariables:{$wtv1,$wtv2}'; - return '{type:\'${super.type}\',payload:{$tm, $tv,$w3mtv}}'; + return '{type:"${super.type}",payload:{$tm, $tv,$w3mtv}}'; } } @@ -313,14 +312,14 @@ class SyncAppData extends MessageData { @override String toString() { final v = 'verified: true'; - final p1 = 'projectId:\'$projectId\''; - final p2 = 'sdkVersion:\'$sdkVersion\''; - final m1 = 'name:\'${metadata.name}\''; - final m2 = 'description:\'${metadata.description}\''; - final m3 = 'url:\'${metadata.url}\''; + final p1 = 'projectId:"$projectId"'; + final p2 = 'sdkVersion:"$sdkVersion"'; + final m1 = 'name:"${metadata.name}"'; + final m2 = 'description:"${metadata.description}"'; + final m3 = 'url:"${metadata.url}"'; final m4 = 'icons:["${metadata.icons.first}"]'; final p3 = 'metadata:{$m1,$m2,$m3,$m4}'; final p = 'payload:{$v,$p1,$p2,$p3}'; - return '{type:\'${super.type}\',$p}'; + return '{type:"${super.type}",$p}'; } } diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart index 3237809..739e7ae 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:reown_appkit/reown_appkit.dart'; class MagicData { @@ -6,6 +7,7 @@ class MagicData { int chainId; ConnectionMetadata? self; ConnectionMetadata? peer; + AppKitSocialOption? provider; MagicData({ required this.email, @@ -13,6 +15,7 @@ class MagicData { required this.address, this.self, this.peer, + this.provider, }); factory MagicData.fromJson(Map json) { @@ -26,6 +29,10 @@ class MagicData { peer: (json['peer'] != null) ? ConnectionMetadata.fromJson(json['peer']) : null, + provider: (json['provider'] != null) + ? AppKitSocialOption.values.firstWhereOrNull((e) => + e.name.toLowerCase() == json['provider'].toString().toLowerCase()) + : null, ); } @@ -36,6 +43,7 @@ class MagicData { 'chainId': chainId, 'self': self?.toJson(), 'peer': peer?.toJson(), + 'provider': provider?.name.toLowerCase(), }; } @@ -48,6 +56,7 @@ class MagicData { int? chainId, ConnectionMetadata? self, ConnectionMetadata? peer, + AppKitSocialOption? provider, }) { return MagicData( email: email ?? this.email, @@ -55,6 +64,7 @@ class MagicData { chainId: chainId ?? this.chainId, self: self ?? this.self, peer: peer ?? this.peer, + provider: provider ?? this.provider, ); } } diff --git a/packages/reown_appkit/lib/modal/widgets/lists/wallets_list.dart b/packages/reown_appkit/lib/modal/widgets/lists/wallets_list.dart index 0b474ac..e90adfe 100644 --- a/packages/reown_appkit/lib/modal/widgets/lists/wallets_list.dart +++ b/packages/reown_appkit/lib/modal/widgets/lists/wallets_list.dart @@ -40,16 +40,24 @@ class WalletsList extends StatelessWidget { ); final walletsListItems = isLoading - ? loadingList + ? loadingList.map( + (listItem) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: listItem, + ), + ) : itemList.map( - (e) => WalletListItem( - onTap: () => onTapWallet?.call(e.data), - showCheckmark: e.data.installed, - imageUrl: e.image, - title: e.title, - trailing: e.data.recent - ? const WalletItemChip(value: ' RECENT ') - : null, + (listItem) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: WalletListItem( + onTap: () => onTapWallet?.call(listItem.data), + showCheckmark: listItem.data.installed, + imageUrl: listItem.image, + title: listItem.title, + trailing: listItem.data.recent + ? const WalletItemChip(value: ' RECENT ') + : null, + ), ), ); final List items = List.from(walletsListItems); @@ -62,7 +70,7 @@ class WalletsList extends StatelessWidget { return ListView.separated( padding: const EdgeInsets.symmetric( - horizontal: kPadding12, + horizontal: kPadding8, vertical: kPadding12, ), itemBuilder: (context, index) { @@ -72,7 +80,7 @@ class WalletsList extends StatelessWidget { ); }, separatorBuilder: (_, index) => SizedBox.square( - dimension: index == 0 ? 0.0 : kListViewSeparatorHeight, + dimension: kListViewSeparatorHeight, ), itemCount: items.length, ); diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart index cd31b7e..02eab5a 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/input_email.dart @@ -87,7 +87,14 @@ class _InputEmailWidgetState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - CircularLoader(size: 20.0, strokeWidth: 2.0), + Row( + children: [ + CircularLoader(size: 20.0, strokeWidth: 2.0), + const SizedBox.square( + dimension: kListViewSeparatorHeight, + ), + ], + ) ], ) : ValueListenableBuilder( From 8667eff4c2a0268e83dedf145b58ddb32c98289a Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 26 Sep 2024 13:53:09 +0200 Subject: [PATCH 04/66] Minor changes --- .github/workflows/release_modal_android.yml | 52 +- .../release_modal_android_internal.yml | 53 +- .github/workflows/release_modal_ios.yml | 48 +- .../workflows/release_modal_ios_internal.yml | 49 +- .gitignore | 3 +- .../android/app/src/main/AndroidManifest.xml | 4 +- .../base/lib/utils/crypto/polkadot.dart | 2 +- .../example/base/lib/utils/crypto/solana.dart | 2 +- .../base/lib/utils/deep_link_handler.dart | 4 +- .../base/lib/utils/smart_contracts.dart | 780 ++++++++++++++++++ .../example/base/lib/utils/test_data.dart | 160 ++++ .../lib/modal/assets/light/apple_logo.svg | 44 +- .../lib/modal/assets/light/discord_logo.svg | 47 +- .../lib/modal/assets/light/facebook_logo.svg | 64 +- .../lib/modal/assets/light/github_logo.svg | 44 +- .../lib/modal/assets/light/x_logo.svg | 38 +- .../lib/modal/i_appkit_modal_impl.dart | 2 + .../magic_service/models/frame_message.dart | 11 +- .../widgets/miscellaneous/searchbar.dart | 23 +- .../lib/modal/widgets/navigation/navbar.dart | 3 + 20 files changed, 1237 insertions(+), 196 deletions(-) diff --git a/.github/workflows/release_modal_android.yml b/.github/workflows/release_modal_android.yml index 908d53c..6aa69fb 100644 --- a/.github/workflows/release_modal_android.yml +++ b/.github/workflows/release_modal_android.yml @@ -11,12 +11,14 @@ on: jobs: build_with_signing: name: Build Android App Release - runs-on: macos-latest + runs-on: macos-latest-xlarge steps: # Checkout the repo - name: Checkout repository uses: actions/checkout@v4 + + # Create temp firebase key - name: Create temp firebase key env: FIREBASE_KEY_BASE64: ${{ secrets.FIREBASE_KEY_BASE64 }} @@ -26,6 +28,7 @@ jobs: # import certificate and provisioning profile from secrets echo -n "$FIREBASE_KEY_BASE64" | base64 --decode -o $FIREBASE_KEY_PATH + # Setup Java 11 - name: Setup Java 17 uses: actions/setup-java@v3 @@ -34,6 +37,8 @@ jobs: java-version: '17' architecture: x86_64 cache: 'gradle' + + # Cache Gradle - name: Cache Gradle uses: actions/cache@v3 with: @@ -43,25 +48,15 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- - # Install Flutter SDK - - name: Install Flutter - uses: subosito/flutter-action@v2 + + # Install Flutter and Dependencies + - uses: ./.github/actions/dependencies with: - flutter-version: '3.19.5' - # Get package dependencies and generate files - - name: Get package dependencies and generate files - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs - # Get example app dependencies and generate files - - name: Get example app dependencies and generate files - working-directory: example - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs + working-directory: packages/reown_appkit/example/modal + # Build Android example app - name: Build Android APK - working-directory: example + working-directory: packages/reown_appkit/example/modal env: PROJECT_ID: ${{ secrets.PROJECT_ID }} APPKIT_AUTH: ${{ secrets.APPKIT_AUTH }} @@ -69,10 +64,10 @@ jobs: AUTH_SERVICE_URL: ${{ secrets.AUTH_SERVICE_URL }} run: | # Get app version from file + # VERSION_FILE=$GITHUB_WORKSPACE/lib/version.dart + # VERSION=`echo $(cat $VERSION_FILE) | sed "s/[^']*'\([^']*\)'.*/\1/"` + VERSION=4.0.0 GRADLE_FILE=android/gradle.properties - VERSION_FILE=$GITHUB_WORKSPACE/lib/version.dart - - VERSION=`echo $(cat $VERSION_FILE) | sed "s/[^']*'\([^']*\)'.*/\1/"` # Set versionName on gradle.properties awk -F"=" -v newval="$VERSION" 'BEGIN{OFS=FS} $1=="versionName"{$2=newval}1' $GRADLE_FILE > "$GRADLE_FILE.tmp" && mv "$GRADLE_FILE.tmp" $GRADLE_FILE @@ -89,18 +84,23 @@ jobs: # Setup Node - name: Setup Node uses: actions/setup-node@v3 + # Setup Firebase - name: Setup Firebase uses: w9jds/setup-firebase@main with: tools-version: 13.0.1 firebase_token: ${{ secrets.FIREBASE_TOKEN }} + + # Upload APK - name: Upload APK - working-directory: example/build/app/outputs/flutter-apk + working-directory: packages/reown_appkit/example/modal/build/app/outputs/flutter-apk env: APP_ID: ${{ secrets.ANDROID_APP_ID }} run: | - firebase appdistribution:distribute app-stable-release.apk --app $APP_ID --release-notes "Web3Modal Flutter stable release" --groups "flutter-team, javascript-team, kotlin-team" + firebase appdistribution:distribute app-stable-release.apk --app $APP_ID --release-notes "AppKit Flutter sample production" --groups "flutter-team, javascript-team, kotlin-team" + + # Notify Channel - name: Notify Channel uses: slackapi/slack-github-action@v1.24.0 env: @@ -109,7 +109,7 @@ jobs: with: payload: |- { - "text":"🤖 New *Android* build *${{ github.ref_name }}* stable version for *Web3Modal Flutter* was just deployed. Test at https://appdistribution.firebase.dev/i/a8efff56e3f0fdb0" + "text":"🤖 AppKit *Modal* Flutter Sample *${{ github.ref_name }}* was just deployed. Test at https://appdistribution.firebase.dev/i/a8efff56e3f0fdb0" } # Clean up Flutter envs @@ -119,4 +119,8 @@ jobs: rm $RUNNER_TEMP/flutter-c7c2c-6df892fe6ddb.json flutter clean cd example - flutter clean \ No newline at end of file + flutter clean + +# Launch locally +# Needs docker to be running +# act -j build_with_signing --container-architecture linux/amd64 -P macos-latest-xlarge=-self-hosted --secret-file .github/workflows/.env.secret.modal -W .github/workflows/release_modal_android.yml \ No newline at end of file diff --git a/.github/workflows/release_modal_android_internal.yml b/.github/workflows/release_modal_android_internal.yml index 29bb0c7..4600df6 100644 --- a/.github/workflows/release_modal_android_internal.yml +++ b/.github/workflows/release_modal_android_internal.yml @@ -11,12 +11,14 @@ on: jobs: build_with_signing: name: Build Android App Internal (beta) - runs-on: macos-latest + runs-on: macos-latest-xlarge steps: # Checkout the repo - name: Checkout repository uses: actions/checkout@v4 + + # Create temp firebase key - name: Create temp firebase key env: FIREBASE_KEY_BASE64: ${{ secrets.FIREBASE_KEY_BASE64 }} @@ -26,6 +28,7 @@ jobs: # import certificate and provisioning profile from secrets echo -n "$FIREBASE_KEY_BASE64" | base64 --decode -o $FIREBASE_KEY_PATH + # Setup Java 11 - name: Setup Java 17 uses: actions/setup-java@v3 @@ -34,6 +37,8 @@ jobs: java-version: '17' architecture: x86_64 cache: 'gradle' + + # Cache Gradle - name: Cache Gradle uses: actions/cache@v3 with: @@ -43,25 +48,15 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- - # Install Flutter SDK - - name: Install Flutter - uses: subosito/flutter-action@v2 + + # Install Flutter and Dependencies + - uses: ./.github/actions/dependencies with: - flutter-version: '3.19.5' - # Get package dependencies and generate files - - name: Get package dependencies and generate files - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs - # Get example app dependencies and generate files - - name: Get example app dependencies and generate files - working-directory: example - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs + working-directory: packages/reown_appkit/example/modal + # Build Android example app - name: Build Android APK - working-directory: example + working-directory: packages/reown_appkit/example/modal env: PROJECT_ID: ${{ secrets.PROJECT_ID }} APPKIT_AUTH: ${{ secrets.APPKIT_AUTH }} @@ -69,10 +64,10 @@ jobs: AUTH_SERVICE_URL: ${{ secrets.AUTH_SERVICE_URL }} run: | # Get app version from file + # VERSION_FILE=$GITHUB_WORKSPACE/lib/version.dart + # VERSION=`echo $(cat $VERSION_FILE) | sed "s/[^']*'\([^']*\)'.*/\1/"` + VERSION=4.0.0 GRADLE_FILE=android/gradle.properties - VERSION_FILE=$GITHUB_WORKSPACE/lib/version.dart - - VERSION=`echo $(cat $VERSION_FILE) | sed "s/[^']*'\([^']*\)'.*/\1/"` # Set versionName on gradle.properties awk -F"=" -v newval="$VERSION" 'BEGIN{OFS=FS} $1=="versionName"{$2=newval}1' $GRADLE_FILE > "$GRADLE_FILE.tmp" && mv "$GRADLE_FILE.tmp" $GRADLE_FILE @@ -89,18 +84,23 @@ jobs: # Setup Node - name: Setup Node uses: actions/setup-node@v3 + # Setup Firebase - name: Setup Firebase uses: w9jds/setup-firebase@main with: tools-version: 13.0.1 firebase_token: ${{ secrets.FIREBASE_TOKEN }} + + # Upload APK - name: Upload APK - working-directory: example/build/app/outputs/flutter-apk + working-directory: packages/reown_appkit/example/modal/build/app/outputs/flutter-apk env: APP_ID: ${{ secrets.ANDROID_APP_ID_INTERNAL }} run: | - firebase appdistribution:distribute app-beta-release.apk --app $APP_ID --release-notes "Web3Modal Flutter beta release" --groups "flutter-team, javascript-team, kotlin-team" + firebase appdistribution:distribute app-beta-release.apk --app $APP_ID --release-notes "AppKit Flutter sample internal" --groups "flutter-team, javascript-team, kotlin-team" + + # Notify Channel - name: Notify Channel uses: slackapi/slack-github-action@v1.24.0 env: @@ -109,8 +109,9 @@ jobs: with: payload: |- { - "text":"🤖 New *Android* build *${{ github.ref_name }}* version for *Web3Modal Flutter* was just deployed. Test at https://appdistribution.firebase.dev/i/a47ee97e86fbdfff" + "text":"🤖 AppKit *Modal* Flutter Sample *Internal* *${{ github.ref_name }}* was just deployed. Test at https://appdistribution.firebase.dev/i/a47ee97e86fbdfff" } + # Clean up Flutter envs - name: Clean up if: ${{ always() }} @@ -118,4 +119,8 @@ jobs: rm $RUNNER_TEMP/flutter-c7c2c-6df892fe6ddb.json flutter clean cd example - flutter clean \ No newline at end of file + flutter clean + +# Launch locally +# Needs docker to be running +# act -j build_with_signing --container-architecture linux/amd64 -P macos-latest-xlarge=-self-hosted --secret-file .github/workflows/.env.secret.modal -W .github/workflows/release_modal_android_internal.yml \ No newline at end of file diff --git a/.github/workflows/release_modal_ios.yml b/.github/workflows/release_modal_ios.yml index c51f444..04ef6c0 100644 --- a/.github/workflows/release_modal_ios.yml +++ b/.github/workflows/release_modal_ios.yml @@ -16,6 +16,7 @@ jobs: # Checkout the repo - name: Checkout repository uses: actions/checkout@v4 + # Install the Apple certificate and provisioning profile - name: Install the Apple certificate and provisioning profile env: @@ -27,7 +28,7 @@ jobs: # create variables BUILD_CERT_PATH=$RUNNER_TEMP/build_certificate.p12 PP_PATH=$RUNNER_TEMP/FlutterAppStoreProfileWithPush.mobileprovision - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + KEYCHAIN_PATH=$RUNNER_TEMP/release-modal.keychain-db # import certificate and provisioning profile from secrets echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $BUILD_CERT_PATH @@ -45,6 +46,7 @@ jobs: # apply provisioning profile mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles + # Create p8 Auth Key from secrets - name: Create p8 Auth Key env: @@ -58,25 +60,15 @@ jobs: # import certificate and provisioning profile from secrets echo -n "$APP_STORE_CONNECT_KEY" | base64 --decode -o $AUTH_KEY_PATH - # Install Flutter SDK - - name: Install Flutter - uses: subosito/flutter-action@v2 + + # Install Flutter and Dependencies + - uses: ./.github/actions/dependencies with: - flutter-version: '3.19.5' - # Get package dependencies and generate files - - name: Get package dependencies and generate files - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs - # Get example app dependencies and generate files - - name: Get example app dependencies and generate files - working-directory: example - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs + working-directory: packages/reown_appkit/example/modal + # Build ios example app - name: Build ios example app - working-directory: example + working-directory: packages/reown_appkit/example/modal env: PROJECT_ID: ${{ secrets.PROJECT_ID }} APP_STORE_KEY_ID: ${{ secrets.APP_STORE_KEY_ID }} @@ -86,10 +78,10 @@ jobs: AUTH_SERVICE_URL: ${{ secrets.AUTH_SERVICE_URL }} run: | # Get app version from file - FILE_VALUE=$(echo | grep "^version: " pubspec.yaml) - PARTS=(${FILE_VALUE//:/ }) - FULL_VERSION=${PARTS[1]} - VERSION_NUMBER=(${FULL_VERSION//-/ }) + # FILE_VALUE=$(echo | grep "^version: " pubspec.yaml) + # PARTS=(${FILE_VALUE//:/ }) + # FULL_VERSION=${PARTS[1]} + VERSION_NUMBER=4.0.0 # Build ios app with flutter flutter build ios --build-name $VERSION_NUMBER --dart-define="PROJECT_ID=$PROJECT_ID" --dart-define="APPKIT_AUTH=$APPKIT_AUTH" --dart-define="APPKIT_PROJECT_ID=$APPKIT_PROJECT_ID" --dart-define="AUTH_SERVICE_URL=$AUTH_SERVICE_URL" --config-only --release @@ -99,8 +91,9 @@ jobs: agvtool next-version -all # Archive and export - xcodebuild -workspace "$GITHUB_WORKSPACE/example/ios/Runner.xcworkspace" -scheme Runner -sdk iphoneos -destination generic/platform=iOS -archivePath "$GITHUB_WORKSPACE/example/ios/Runner.xcarchive" archive - xcodebuild -exportArchive -allowProvisioningUpdates -sdk iphoneos -archivePath "$GITHUB_WORKSPACE/example/ios/Runner.xcarchive" -exportOptionsPlist "$GITHUB_WORKSPACE/example/ios/Runner/ExportOptionsRelease.plist" -exportPath "$GITHUB_WORKSPACE/example/build/ios/ipa" -authenticationKeyIssuerID $APPLE_ISSUER_ID -authenticationKeyID $APP_STORE_KEY_ID -authenticationKeyPath "$GITHUB_WORKSPACE/example/build/ios/ipa/private_keys/AuthKey_$APP_STORE_KEY_ID.p8" + xcodebuild -workspace "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/ios/Runner.xcworkspace" -scheme Runner -sdk iphoneos -destination generic/platform=iOS -archivePath "$GITHUB_WORKSPACE/example/ios/Runner.xcarchive" archive + xcodebuild -exportArchive -allowProvisioningUpdates -sdk iphoneos -archivePath "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/ios/Runner.xcarchive" -exportOptionsPlist "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/ios/Runner/ExportOptionsInternal.plist" -exportPath "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/build/ios/ipa" -authenticationKeyIssuerID $APPLE_ISSUER_ID -authenticationKeyID $APP_STORE_KEY_ID -authenticationKeyPath "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/build/ios/ipa/private_keys/AuthKey_$APP_STORE_KEY_ID.p8" + # Upload IPA to Testflight - name: Upload IPA to Testflight working-directory: example/build/ios/ipa @@ -108,6 +101,8 @@ jobs: APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} APP_STORE_KEY_ID: ${{ secrets.APP_STORE_KEY_ID }} run: xcrun altool --upload-app --type ios -f web3modal_flutter.ipa --apiKey $APP_STORE_KEY_ID --apiIssuer $APPLE_ISSUER_ID + + # Notify Channel - name: Notify Channel uses: slackapi/slack-github-action@v1.24.0 env: @@ -116,13 +111,14 @@ jobs: with: payload: |- { - "text":"🍎 New *iOS* build *${{ github.ref_name }}* stable version for *Web3Modal Flutter* was just deployed." + "text":"🍎 AppKit *Modal* Flutter Sample *${{ github.ref_name }}* was just deployed." } - # Clean up + + # # Clean up - name: Clean up if: ${{ always() }} run: | - security delete-keychain $RUNNER_TEMP/app-signing.keychain-db + security delete-keychain $RUNNER_TEMP/release-modal.keychain-db rm ~/Library/MobileDevice/Provisioning\ Profiles/FlutterAppStoreProfileWithPush.mobileprovision flutter clean cd example diff --git a/.github/workflows/release_modal_ios_internal.yml b/.github/workflows/release_modal_ios_internal.yml index e6d75b8..c1c9ebf 100644 --- a/.github/workflows/release_modal_ios_internal.yml +++ b/.github/workflows/release_modal_ios_internal.yml @@ -16,6 +16,7 @@ jobs: # Checkout the repo - name: Checkout repository uses: actions/checkout@v4 + # Install the Apple certificate and provisioning profile - name: Install the Apple certificate and provisioning profile env: @@ -27,7 +28,7 @@ jobs: # create variables BUILD_CERT_PATH=$RUNNER_TEMP/build_certificate.p12 PP_PATH=$RUNNER_TEMP/FlutterAppStoreProfileInternal.mobileprovision - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + KEYCHAIN_PATH=$RUNNER_TEMP/release-modal.keychain-db # import certificate and provisioning profile from secrets echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $BUILD_CERT_PATH @@ -45,6 +46,7 @@ jobs: # apply provisioning profile mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles + # Create p8 Auth Key from secrets - name: Create p8 Auth Key env: @@ -58,25 +60,15 @@ jobs: # import certificate and provisioning profile from secrets echo -n "$APP_STORE_CONNECT_KEY" | base64 --decode -o $AUTH_KEY_PATH - # Install Flutter SDK - - name: Install Flutter - uses: subosito/flutter-action@v2 + + # Install Flutter and Dependencies + - uses: ./.github/actions/dependencies with: - flutter-version: '3.19.5' - # Get package dependencies and generate files - - name: Get package dependencies and generate files - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs - # Get example app dependencies and generate files - - name: Get example app dependencies and generate files - working-directory: example - run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs + working-directory: packages/reown_appkit/example/modal + # Build ios example app - name: Build ios example app - working-directory: example + working-directory: packages/reown_appkit/example/modal env: PROJECT_ID: ${{ secrets.PROJECT_ID }} APP_STORE_KEY_ID: ${{ secrets.APP_STORE_KEY_ID }} @@ -86,10 +78,10 @@ jobs: AUTH_SERVICE_URL: ${{ secrets.AUTH_SERVICE_URL }} run: | # Get app version from file - FILE_VALUE=$(echo | grep "^version: " pubspec.yaml) - PARTS=(${FILE_VALUE//:/ }) - FULL_VERSION=${PARTS[1]} - VERSION_NUMBER=(${FULL_VERSION//-/ }) + # FILE_VALUE=$(echo | grep "^version: " pubspec.yaml) + # PARTS=(${FILE_VALUE//:/ }) + # FULL_VERSION=${PARTS[1]} + VERSION_NUMBER=4.0.0 # Change bundleId in Runner scheme sed -i '' 's/com.web3modal.flutterExample/com.web3modal.flutterExample.internal/g' ios/Runner.xcodeproj/project.pbxproj @@ -106,8 +98,9 @@ jobs: agvtool next-version -all # Archive and export - xcodebuild -workspace "$GITHUB_WORKSPACE/example/ios/Runner.xcworkspace" -scheme Runner -sdk iphoneos -destination generic/platform=iOS -archivePath "$GITHUB_WORKSPACE/example/ios/Runner.xcarchive" archive - xcodebuild -exportArchive -allowProvisioningUpdates -sdk iphoneos -archivePath "$GITHUB_WORKSPACE/example/ios/Runner.xcarchive" -exportOptionsPlist "$GITHUB_WORKSPACE/example/ios/Runner/ExportOptionsInternal.plist" -exportPath "$GITHUB_WORKSPACE/example/build/ios/ipa" -authenticationKeyIssuerID $APPLE_ISSUER_ID -authenticationKeyID $APP_STORE_KEY_ID -authenticationKeyPath "$GITHUB_WORKSPACE/example/build/ios/ipa/private_keys/AuthKey_$APP_STORE_KEY_ID.p8" + xcodebuild -workspace "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/ios/Runner.xcworkspace" -scheme Runner -sdk iphoneos -destination generic/platform=iOS -archivePath "$GITHUB_WORKSPACE/example/ios/Runner.xcarchive" archive + xcodebuild -exportArchive -allowProvisioningUpdates -sdk iphoneos -archivePath "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/ios/Runner.xcarchive" -exportOptionsPlist "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/ios/Runner/ExportOptionsInternal.plist" -exportPath "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/build/ios/ipa" -authenticationKeyIssuerID $APPLE_ISSUER_ID -authenticationKeyID $APP_STORE_KEY_ID -authenticationKeyPath "$GITHUB_WORKSPACE/packages/reown_appkit/example/modal/build/ios/ipa/private_keys/AuthKey_$APP_STORE_KEY_ID.p8" + # Upload IPA to Testflight - name: Upload IPA to Testflight working-directory: example/build/ios/ipa @@ -116,6 +109,8 @@ jobs: APP_STORE_KEY_ID: ${{ secrets.APP_STORE_KEY_ID }} run: | xcrun altool --upload-app --type ios -f web3modal_flutter.ipa --apiKey $APP_STORE_KEY_ID --apiIssuer $APPLE_ISSUER_ID + + # Notify Channel - name: Notify Channel uses: slackapi/slack-github-action@v1.24.0 env: @@ -124,14 +119,18 @@ jobs: with: payload: |- { - "text":"🍎 New *iOS* build *${{ github.ref_name }}* version for *Web3Modal Flutter* was just deployed. Test at https://testflight.apple.com/join/pzF2SUVm" + "text":"🍎 AppKit *Modal* Flutter Sample *${{ github.ref_name }}-internal* was just deployed. Test at https://testflight.apple.com/join/pzF2SUVm" } + # Clean up - name: Clean up if: ${{ always() }} run: | - security delete-keychain $RUNNER_TEMP/app-signing.keychain-db + security delete-keychain $RUNNER_TEMP/release-modal.keychain-db rm ~/Library/MobileDevice/Provisioning\ Profiles/FlutterAppStoreProfileInternal.mobileprovision flutter clean cd example flutter clean + +# Launch locally +# act -j build_with_signing --container-architecture linux/amd64 -P macos-latest-xlarge=-self-hosted --secret-file .github/workflows/.env.secret.modal -W .github/workflows/release_modal_ios_internal.yml \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2819f6d..32c2797 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,5 @@ app.*.map.json # Run scripts run_tests_all.sh -*.env.secret +*.env.secret* + diff --git a/packages/reown_appkit/example/base/android/app/src/main/AndroidManifest.xml b/packages/reown_appkit/example/base/android/app/src/main/AndroidManifest.xml index fe850f8..368da92 100644 --- a/packages/reown_appkit/example/base/android/app/src/main/AndroidManifest.xml +++ b/packages/reown_appkit/example/base/android/app/src/main/AndroidManifest.xml @@ -52,8 +52,8 @@ - - + + events = {}; static Future callMethod({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String method, required String chainId, diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart b/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart index 329d83c..a871e33 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/solana.dart @@ -24,7 +24,7 @@ class Solana { static final Map events = {}; static Future callMethod({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String method, required ChainMetadata chainData, diff --git a/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart b/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart index 7e22515..ccf15e9 100644 --- a/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart +++ b/packages/reown_appkit/example/base/lib/utils/deep_link_handler.dart @@ -20,7 +20,7 @@ class DeepLinkHandler { onError: _onError, ); } catch (e) { - debugPrint('[SampleWallet] [DeepLinkHandler] checkInitialLink $e'); + debugPrint('[SampleDapp] checkInitialLink $e'); } } @@ -34,7 +34,7 @@ class DeepLinkHandler { try { _methodChannel.invokeMethod('initialLink'); } catch (e) { - debugPrint('[SampleWallet] [DeepLinkHandler] checkInitialLink $e'); + debugPrint('[SampleDapp] checkInitialLink $e'); } } diff --git a/packages/reown_appkit/example/base/lib/utils/smart_contracts.dart b/packages/reown_appkit/example/base/lib/utils/smart_contracts.dart index 9b20505..594d8a1 100644 --- a/packages/reown_appkit/example/base/lib/utils/smart_contracts.dart +++ b/packages/reown_appkit/example/base/lib/utils/smart_contracts.dart @@ -277,3 +277,783 @@ class SepoliaTestContract { } ]; } + +class AAVESepoliaContract { + // AAVE on Sepolia + // https://sepolia.etherscan.io/token/0x88541670E55cC00bEEFD87eB59EDd1b7C511AC9a + static const contractAddress = '0x88541670E55cC00bEEFD87eB59EDd1b7C511AC9a'; + + static const contractABI = [ + { + 'inputs': [ + {'internalType': 'string', 'name': 'name', 'type': 'string'}, + {'internalType': 'string', 'name': 'symbol', 'type': 'string'}, + {'internalType': 'uint8', 'name': 'decimals', 'type': 'uint8'}, + {'internalType': 'address', 'name': 'owner', 'type': 'address'} + ], + 'stateMutability': 'nonpayable', + 'type': 'constructor' + }, + { + 'anonymous': false, + 'inputs': [ + { + 'indexed': true, + 'internalType': 'address', + 'name': 'owner', + 'type': 'address' + }, + { + 'indexed': true, + 'internalType': 'address', + 'name': 'spender', + 'type': 'address' + }, + { + 'indexed': false, + 'internalType': 'uint256', + 'name': 'value', + 'type': 'uint256' + } + ], + 'name': 'Approval', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + { + 'indexed': true, + 'internalType': 'address', + 'name': 'previousOwner', + 'type': 'address' + }, + { + 'indexed': true, + 'internalType': 'address', + 'name': 'newOwner', + 'type': 'address' + } + ], + 'name': 'OwnershipTransferred', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + { + 'indexed': true, + 'internalType': 'address', + 'name': 'from', + 'type': 'address' + }, + { + 'indexed': true, + 'internalType': 'address', + 'name': 'to', + 'type': 'address' + }, + { + 'indexed': false, + 'internalType': 'uint256', + 'name': 'value', + 'type': 'uint256' + } + ], + 'name': 'Transfer', + 'type': 'event' + }, + { + 'inputs': [], + 'name': 'DOMAIN_SEPARATOR', + 'outputs': [ + {'internalType': 'bytes32', 'name': '', 'type': 'bytes32'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'EIP712_REVISION', + 'outputs': [ + {'internalType': 'bytes', 'name': '', 'type': 'bytes'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'PERMIT_TYPEHASH', + 'outputs': [ + {'internalType': 'bytes32', 'name': '', 'type': 'bytes32'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'owner', 'type': 'address'}, + {'internalType': 'address', 'name': 'spender', 'type': 'address'} + ], + 'name': 'allowance', + 'outputs': [ + {'internalType': 'uint256', 'name': '', 'type': 'uint256'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'spender', 'type': 'address'}, + {'internalType': 'uint256', 'name': 'amount', 'type': 'uint256'} + ], + 'name': 'approve', + 'outputs': [ + {'internalType': 'bool', 'name': '', 'type': 'bool'} + ], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'account', 'type': 'address'} + ], + 'name': 'balanceOf', + 'outputs': [ + {'internalType': 'uint256', 'name': '', 'type': 'uint256'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'decimals', + 'outputs': [ + {'internalType': 'uint8', 'name': '', 'type': 'uint8'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'spender', 'type': 'address'}, + { + 'internalType': 'uint256', + 'name': 'subtractedValue', + 'type': 'uint256' + } + ], + 'name': 'decreaseAllowance', + 'outputs': [ + {'internalType': 'bool', 'name': '', 'type': 'bool'} + ], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'spender', 'type': 'address'}, + {'internalType': 'uint256', 'name': 'addedValue', 'type': 'uint256'} + ], + 'name': 'increaseAllowance', + 'outputs': [ + {'internalType': 'bool', 'name': '', 'type': 'bool'} + ], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'account', 'type': 'address'}, + {'internalType': 'uint256', 'name': 'value', 'type': 'uint256'} + ], + 'name': 'mint', + 'outputs': [ + {'internalType': 'bool', 'name': '', 'type': 'bool'} + ], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'uint256', 'name': 'value', 'type': 'uint256'} + ], + 'name': 'mint', + 'outputs': [ + {'internalType': 'bool', 'name': '', 'type': 'bool'} + ], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'name', + 'outputs': [ + {'internalType': 'string', 'name': '', 'type': 'string'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'owner', 'type': 'address'} + ], + 'name': 'nonces', + 'outputs': [ + {'internalType': 'uint256', 'name': '', 'type': 'uint256'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'owner', + 'outputs': [ + {'internalType': 'address', 'name': '', 'type': 'address'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'owner', 'type': 'address'}, + {'internalType': 'address', 'name': 'spender', 'type': 'address'}, + {'internalType': 'uint256', 'name': 'value', 'type': 'uint256'}, + {'internalType': 'uint256', 'name': 'deadline', 'type': 'uint256'}, + {'internalType': 'uint8', 'name': 'v', 'type': 'uint8'}, + {'internalType': 'bytes32', 'name': 'r', 'type': 'bytes32'}, + {'internalType': 'bytes32', 'name': 's', 'type': 'bytes32'} + ], + 'name': 'permit', + 'outputs': [], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'renounceOwnership', + 'outputs': [], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'symbol', + 'outputs': [ + {'internalType': 'string', 'name': '', 'type': 'string'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [], + 'name': 'totalSupply', + 'outputs': [ + {'internalType': 'uint256', 'name': '', 'type': 'uint256'} + ], + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'recipient', 'type': 'address'}, + {'internalType': 'uint256', 'name': 'amount', 'type': 'uint256'} + ], + 'name': 'transfer', + 'outputs': [ + {'internalType': 'bool', 'name': '', 'type': 'bool'} + ], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'sender', 'type': 'address'}, + {'internalType': 'address', 'name': 'recipient', 'type': 'address'}, + {'internalType': 'uint256', 'name': 'amount', 'type': 'uint256'} + ], + 'name': 'transferFrom', + 'outputs': [ + {'internalType': 'bool', 'name': '', 'type': 'bool'} + ], + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + {'internalType': 'address', 'name': 'newOwner', 'type': 'address'} + ], + 'name': 'transferOwnership', + 'outputs': [], + 'stateMutability': 'nonpayable', + 'type': 'function' + } + ]; +} + +class USDTContract { + // USDT-ERC20 + // https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7 + static const contractAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; + + static const contractABI = [ + { + 'constant': true, + 'inputs': [], + 'name': 'name', + 'outputs': [ + {'name': '', 'type': 'string'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': '_upgradedAddress', 'type': 'address'} + ], + 'name': 'deprecate', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': '_spender', 'type': 'address'}, + {'name': '_value', 'type': 'uint256'} + ], + 'name': 'approve', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'deprecated', + 'outputs': [ + {'name': '', 'type': 'bool'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': '_evilUser', 'type': 'address'} + ], + 'name': 'addBlackList', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'totalSupply', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': '_from', 'type': 'address'}, + {'name': '_to', 'type': 'address'}, + {'name': '_value', 'type': 'uint256'} + ], + 'name': 'transferFrom', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'upgradedAddress', + 'outputs': [ + {'name': '', 'type': 'address'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [ + {'name': '', 'type': 'address'} + ], + 'name': 'balances', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'decimals', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'maximumFee', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': '_totalSupply', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [], + 'name': 'unpause', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [ + {'name': '_maker', 'type': 'address'} + ], + 'name': 'getBlackListStatus', + 'outputs': [ + {'name': '', 'type': 'bool'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [ + {'name': '', 'type': 'address'}, + {'name': '', 'type': 'address'} + ], + 'name': 'allowed', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'paused', + 'outputs': [ + {'name': '', 'type': 'bool'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [ + {'name': 'who', 'type': 'address'} + ], + 'name': 'balanceOf', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [], + 'name': 'pause', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'getOwner', + 'outputs': [ + {'name': '', 'type': 'address'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'owner', + 'outputs': [ + {'name': '', 'type': 'address'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'symbol', + 'outputs': [ + {'name': '', 'type': 'string'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': '_to', 'type': 'address'}, + {'name': '_value', 'type': 'uint256'} + ], + 'name': 'transfer', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': 'newBasisPoints', 'type': 'uint256'}, + {'name': 'newMaxFee', 'type': 'uint256'} + ], + 'name': 'setParams', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': 'amount', 'type': 'uint256'} + ], + 'name': 'issue', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': 'amount', 'type': 'uint256'} + ], + 'name': 'redeem', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [ + {'name': '_owner', 'type': 'address'}, + {'name': '_spender', 'type': 'address'} + ], + 'name': 'allowance', + 'outputs': [ + {'name': 'remaining', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'basisPointsRate', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [ + {'name': '', 'type': 'address'} + ], + 'name': 'isBlackListed', + 'outputs': [ + {'name': '', 'type': 'bool'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': '_clearedUser', 'type': 'address'} + ], + 'name': 'removeBlackList', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'MAX_UINT', + 'outputs': [ + {'name': '', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': 'newOwner', 'type': 'address'} + ], + 'name': 'transferOwnership', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + {'name': '_blackListedUser', 'type': 'address'} + ], + 'name': 'destroyBlackFunds', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + {'name': '_initialSupply', 'type': 'uint256'}, + {'name': '_name', 'type': 'string'}, + {'name': '_symbol', 'type': 'string'}, + {'name': '_decimals', 'type': 'uint256'} + ], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'constructor' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': false, 'name': 'amount', 'type': 'uint256'} + ], + 'name': 'Issue', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': false, 'name': 'amount', 'type': 'uint256'} + ], + 'name': 'Redeem', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': false, 'name': 'newAddress', 'type': 'address'} + ], + 'name': 'Deprecate', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': false, 'name': 'feeBasisPoints', 'type': 'uint256'}, + {'indexed': false, 'name': 'maxFee', 'type': 'uint256'} + ], + 'name': 'Params', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': false, 'name': '_blackListedUser', 'type': 'address'}, + {'indexed': false, 'name': '_balance', 'type': 'uint256'} + ], + 'name': 'DestroyedBlackFunds', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': false, 'name': '_user', 'type': 'address'} + ], + 'name': 'AddedBlackList', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': false, 'name': '_user', 'type': 'address'} + ], + 'name': 'RemovedBlackList', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': true, 'name': 'owner', 'type': 'address'}, + {'indexed': true, 'name': 'spender', 'type': 'address'}, + {'indexed': false, 'name': 'value', 'type': 'uint256'} + ], + 'name': 'Approval', + 'type': 'event' + }, + { + 'anonymous': false, + 'inputs': [ + {'indexed': true, 'name': 'from', 'type': 'address'}, + {'indexed': true, 'name': 'to', 'type': 'address'}, + {'indexed': false, 'name': 'value', 'type': 'uint256'} + ], + 'name': 'Transfer', + 'type': 'event' + }, + {'anonymous': false, 'inputs': [], 'name': 'Pause', 'type': 'event'}, + {'anonymous': false, 'inputs': [], 'name': 'Unpause', 'type': 'event'} + ]; +} diff --git a/packages/reown_appkit/example/base/lib/utils/test_data.dart b/packages/reown_appkit/example/base/lib/utils/test_data.dart index cb78e1d..636ea9a 100644 --- a/packages/reown_appkit/example/base/lib/utils/test_data.dart +++ b/packages/reown_appkit/example/base/lib/utils/test_data.dart @@ -36,6 +36,166 @@ String testSignTypedData(String address) => jsonEncode( const typedData = r'''{"types":{"EIP712Domain":[{"type":"string","name":"name"},{"type":"string","name":"version"},{"type":"uint256","name":"chainId"},{"type":"address","name":"verifyingContract"}],"Part":[{"name":"account","type":"address"},{"name":"value","type":"uint96"}],"Mint721":[{"name":"tokenId","type":"uint256"},{"name":"tokenURI","type":"string"},{"name":"creators","type":"Part[]"},{"name":"royalties","type":"Part[]"}]},"domain":{"name":"Mint721","version":"1","chainId":4,"verifyingContract":"0x2547760120aed692eb19d22a5d9ccfe0f7872fce"},"primaryType":"Mint721","message":{"@type":"ERC721","contract":"0x2547760120aed692eb19d22a5d9ccfe0f7872fce","tokenId":"1","uri":"ipfs://ipfs/hash","creators":[{"account":"0xc5eac3488524d577a1495492599e8013b1f91efa","value":10000}],"royalties":[],"tokenURI":"ipfs://ipfs/hash"}}'''; +Map typeDataV3(int chainId) => { + 'types': { + 'EIP712Domain': [ + {'name': 'name', 'type': 'string'}, + {'name': 'version', 'type': 'string'}, + {'name': 'chainId', 'type': 'uint256'}, + {'name': 'verifyingContract', 'type': 'address'} + ], + 'Person': [ + {'name': 'name', 'type': 'string'}, + {'name': 'wallet', 'type': 'address'} + ], + 'Mail': [ + {'name': 'from', 'type': 'Person'}, + {'name': 'to', 'type': 'Person'}, + {'name': 'contents', 'type': 'string'} + ] + }, + 'primaryType': 'Mail', + 'domain': { + 'name': 'Ether Mail', + 'version': '1', + 'chainId': chainId, + 'verifyingContract': '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' + }, + 'message': { + 'from': { + 'name': 'Cow', + 'wallet': '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826' + }, + 'to': { + 'name': 'Bob', + 'wallet': '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' + }, + 'contents': 'Hello, Bob!' + } + }; + +Map typeDataV4(int chainId) => { + 'types': { + 'EIP712Domain': [ + {'type': 'string', 'name': 'name'}, + {'type': 'string', 'name': 'version'}, + {'type': 'uint256', 'name': 'chainId'}, + {'type': 'address', 'name': 'verifyingContract'} + ], + 'Part': [ + {'name': 'account', 'type': 'address'}, + {'name': 'value', 'type': 'uint96'} + ], + 'Mint721': [ + {'name': 'tokenId', 'type': 'uint256'}, + {'name': 'tokenURI', 'type': 'string'}, + {'name': 'creators', 'type': 'Part[]'}, + {'name': 'royalties', 'type': 'Part[]'} + ] + }, + 'domain': { + 'name': 'Mint721', + 'version': '1', + 'chainId': chainId, + 'verifyingContract': '0x2547760120aed692eb19d22a5d9ccfe0f7872fce' + }, + 'primaryType': 'Mint721', + 'message': { + '@type': 'ERC721', + 'contract': '0x2547760120aed692eb19d22a5d9ccfe0f7872fce', + 'tokenId': '1', + 'uri': 'ipfs://ipfs/hash', + 'creators': [ + { + 'account': '0xc5eac3488524d577a1495492599e8013b1f91efa', + 'value': 10000 + } + ], + 'royalties': [], + 'tokenURI': 'ipfs://ipfs/hash' + } + }; + +/// KADENA /// + +// SignRequest createSignRequest({ +// required String networkId, +// required String signingPubKey, +// required String sender, +// String code = '"hello"', +// Map? data, +// List caps = const [], +// String chainId = '1', +// int gasLimit = 2000, +// double gasPrice = 1e-8, +// int ttl = 600, +// }) => +// SignRequest( +// code: code, +// data: data ?? {}, +// sender: sender, +// networkId: networkId, +// chainId: chainId, +// gasLimit: gasLimit, +// gasPrice: gasPrice, +// signingPubKey: signingPubKey, +// ttl: ttl, +// caps: caps, +// ); + +// PactCommandPayload createPactCommandPayload({ +// required String networkId, +// required String sender, +// String code = '"hello"', +// Map? data, +// List signerCaps = const [], +// String chainId = '1', +// int gasLimit = 2000, +// double gasPrice = 1e-8, +// int ttl = 600, +// }) => +// PactCommandPayload( +// networkId: networkId, +// payload: CommandPayload( +// exec: ExecMessage( +// code: code, +// data: data ?? {}, +// ), +// ), +// signers: signerCaps, +// meta: CommandMetadata( +// chainId: chainId, +// gasLimit: gasLimit, +// gasPrice: gasPrice, +// ttl: ttl, +// sender: sender, +// ), +// ); + +// QuicksignRequest createQuicksignRequest({ +// required String cmd, +// List sigs = const [], +// }) => +// QuicksignRequest( +// commandSigDatas: [ +// CommandSigData( +// cmd: cmd, +// sigs: sigs, +// ), +// ], +// ); + +// GetAccountsRequest createGetAccountsRequest({ +// required String account, +// }) => +// GetAccountsRequest( +// accounts: [ +// AccountRequest( +// account: account, +// ), +// ], +// ); + /// KADENA /// // SignRequest createSignRequest({ diff --git a/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg b/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg index b32ec0d..b9c0a52 100644 --- a/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/apple_logo.svg @@ -1,18 +1,28 @@ - - - - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg b/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg index c1297ca..d3e8e7d 100644 --- a/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/discord_logo.svg @@ -1,17 +1,32 @@ - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg b/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg index 36d6d69..e100f2b 100644 --- a/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/facebook_logo.svg @@ -1,26 +1,40 @@ - - - - - - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/github_logo.svg b/packages/reown_appkit/lib/modal/assets/light/github_logo.svg index e8f413a..66bf7fc 100644 --- a/packages/reown_appkit/lib/modal/assets/light/github_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/github_logo.svg @@ -1,18 +1,28 @@ - - - - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/x_logo.svg b/packages/reown_appkit/lib/modal/assets/light/x_logo.svg index 8fbeb50..11ff04c 100644 --- a/packages/reown_appkit/lib/modal/assets/light/x_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/x_logo.svg @@ -1,12 +1,28 @@ - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart index 6372545..f358a24 100644 --- a/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/i_appkit_modal_impl.dart @@ -145,6 +145,8 @@ abstract class IReownAppKitModal with ChangeNotifier { Future requestSwitchToChain(ReownAppKitModalNetworkInfo newChain); Future requestAddChain(ReownAppKitModalNetworkInfo newChain); + Future dispatchEnvelope(String url); + /// Closes the modal. void closeModal({bool disconnectSession = false}); diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart index a611f7f..bb22886 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/frame_message.dart @@ -248,6 +248,11 @@ class RpcRequest extends MessageData { final address = p.last; return '{$t,payload:{$m,params:["$data","$address"]}}'; } + if (method == 'eth_sign') { + final address = p.first; + final data = p.last; + return '{$t,payload:{$m,params:["$address","$data"]}}'; + } if (method == 'eth_signTypedData_v4' || method == 'eth_signTypedData_v3' || method == 'eth_signTypedData') { @@ -318,7 +323,11 @@ class SyncAppData extends MessageData { final m2 = 'description:"${metadata.description}"'; final m3 = 'url:"${metadata.url}"'; final m4 = 'icons:["${metadata.icons.first}"]'; - final p3 = 'metadata:{$m1,$m2,$m3,$m4}'; + final r1 = 'native:"${metadata.redirect?.native}"'; + final r2 = 'universal:"${metadata.redirect?.universal}"'; + final r3 = 'linkMode:"${metadata.redirect?.linkMode}"'; + final m5 = 'redirect:{$r1,$r2,$r3}'; + final p3 = 'metadata:{$m1,$m2,$m3,$m4,$m5}'; final p = 'payload:{$v,$p1,$p2,$p3}'; return '{type:"${super.type}",$p}'; } diff --git a/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart b/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart index 1a41caf..f3bd868 100644 --- a/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart +++ b/packages/reown_appkit/lib/modal/widgets/miscellaneous/searchbar.dart @@ -112,9 +112,15 @@ class _ModalSearchBarState extends State void _setDecoration() { final themeColors = ReownAppKitModalTheme.colorsOf(context); + final radiuses = ReownAppKitModalTheme.radiusesOf(context); _decorationTween = DecorationTween( begin: BoxDecoration( - borderRadius: BorderRadius.circular(widget.height * 0.4), + // borderRadius: BorderRadius.circular(widget.height * 0.4), + borderRadius: radiuses.isSquare() + ? BorderRadius.zero + : (radiuses.isCircular() + ? BorderRadius.circular(widget.height) + : BorderRadius.circular(widget.height * 0.4)), boxShadow: [ BoxShadow( color: Colors.transparent, @@ -126,7 +132,12 @@ class _ModalSearchBarState extends State ], ), end: BoxDecoration( - borderRadius: BorderRadius.circular(widget.height * 0.4), + // borderRadius: BorderRadius.circular(widget.height * 0.4), + borderRadius: radiuses.isSquare() + ? BorderRadius.zero + : (radiuses.isCircular() + ? BorderRadius.circular(widget.height) + : BorderRadius.circular(widget.height * 0.4)), boxShadow: [ BoxShadow( color: themeColors.accenGlass015, @@ -173,9 +184,15 @@ class _ModalSearchBarState extends State Widget build(BuildContext context) { final themeData = ReownAppKitModalTheme.getDataOf(context); final themeColors = ReownAppKitModalTheme.colorsOf(context); + final radiuses = ReownAppKitModalTheme.radiusesOf(context); final unfocusedBorder = OutlineInputBorder( borderSide: BorderSide(color: themeColors.grayGlass005, width: 1.0), - borderRadius: BorderRadius.circular(widget.height * 0.3), + // borderRadius: BorderRadius.circular(widget.height * 0.3), + borderRadius: radiuses.isSquare() + ? BorderRadius.zero + : (radiuses.isCircular() + ? BorderRadius.circular(widget.height) + : BorderRadius.circular(widget.height * 0.3)), ); final focusedBorder = unfocusedBorder.copyWith( borderSide: BorderSide(color: themeColors.accent100, width: 1.0), diff --git a/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart b/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart index bde4413..605c79d 100644 --- a/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart +++ b/packages/reown_appkit/lib/modal/widgets/navigation/navbar.dart @@ -14,6 +14,7 @@ class ModalNavbar extends StatelessWidget { required this.body, required this.title, this.leftAction, + this.rightAction, this.safeAreaLeft = false, this.safeAreaRight = false, this.safeAreaBottom = true, @@ -26,6 +27,7 @@ class ModalNavbar extends StatelessWidget { final Widget body; final String title; final NavbarActionButton? leftAction; + final NavbarActionButton? rightAction; final bool safeAreaLeft, safeAreaRight, safeAreaBottom, noClose, divider; @override @@ -82,6 +84,7 @@ class ModalNavbar extends StatelessWidget { ModalProvider.of(context).instance.closeModal(); }, ), + rightAction ?? SizedBox.shrink(), ], ); }, From 4c863b5dfea47b4239fd32ab9c54b993ec5fc466 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 26 Sep 2024 14:36:35 +0200 Subject: [PATCH 05/66] Implemented webview based services --- packages/reown_appkit/CHANGELOG.md | 4 + .../base/ios/Runner.xcodeproj/project.pbxproj | 92 +++--- .../reown_appkit/example/base/lib/main.dart | 34 +- .../example/base/lib/pages/connect_page.dart | 70 +++- .../example/base/lib/pages/pairings_page.dart | 27 +- .../example/base/lib/pages/sessions_page.dart | 47 +-- .../example/base/lib/utils/crypto/eip155.dart | 116 ++++--- .../base/lib/widgets/session_widget.dart | 58 ++-- .../ios/Runner.xcodeproj/project.pbxproj | 114 +++---- .../modal/ios/Runner/AppDelegate.swift | 93 +++++- .../modal/ios/Runner/Runner.entitlements | 6 +- .../example/modal/lib/home_page.dart | 20 +- .../reown_appkit/example/modal/lib/main.dart | 3 + .../modal/lib/services/deep_link_handler.dart | 57 ++++ .../modal/lib/widgets/debug_drawer.dart | 129 ++++++-- .../modal/lib/widgets/session_widget.dart | 23 +- .../lib/modal/appkit_modal_impl.dart | 93 ++++-- .../models/public/appkit_modal_session.dart | 64 ++-- .../models/public/appkit_social_options.dart | 6 +- .../lib/modal/pages/all_social_logins.dart | 93 ++++++ .../pages/approve_magic_request_page.dart | 2 +- .../appkit_modal_main_wallets_page.dart | 92 ++++-- .../lib/modal/pages/social_login_page.dart | 304 ++++++++++++++---- .../magic_service/i_magic_service.dart | 5 +- .../services/magic_service/magic_service.dart | 177 ++++++---- .../magic_service/models/magic_data.dart | 22 +- .../magic_service/models/magic_events.dart | 5 + .../buttons/email_login_input_field.dart | 2 +- .../buttons/social_login_buttons_view.dart | 209 ++++++++---- .../public/appkit_modal_connect_button.dart | 5 +- packages/reown_appkit/lib/version.dart | 2 +- packages/reown_appkit/pubspec.yaml | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 86 ++--- 33 files changed, 1449 insertions(+), 613 deletions(-) create mode 100644 packages/reown_appkit/example/modal/lib/services/deep_link_handler.dart create mode 100644 packages/reown_appkit/lib/modal/pages/all_social_logins.dart diff --git a/packages/reown_appkit/CHANGELOG.md b/packages/reown_appkit/CHANGELOG.md index 2b6d35a..1aa0966 100644 --- a/packages/reown_appkit/CHANGELOG.md +++ b/packages/reown_appkit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.0 + +- Social Logins + ## 1.0.1 - Updated Coinbase Wallet SDK to support Android Gradle Plugin 8 diff --git a/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj b/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj index 8233dbe..6924d25 100644 --- a/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj @@ -9,12 +9,12 @@ /* Begin PBXBuildFile section */ 0964B3132C49545400AE1CDA /* Info-internal.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0964B3122C49545400AE1CDA /* Info-internal.plist */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 1B5DEA3E6EDC27B89AF2AB05 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B56F69AB969AA03109DA32E /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - DC20FE362923283F45816BFC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 787ABA0FE3B824874C8F17D8 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -40,14 +40,13 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3BC12F23EEF9AAEBD6DC9470 /* Pods-Runner.debug-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-internal.xcconfig"; sourceTree = ""; }; - 502CCCDE3EE7AF774B525040 /* Pods-Runner.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-production.xcconfig"; sourceTree = ""; }; - 6CC9F567B7A0A0781351BF49 /* Pods-Runner.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-internal.xcconfig"; sourceTree = ""; }; + 5830A10298A0E37E0DEACD59 /* Pods-Runner.profile-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-internal.xcconfig"; sourceTree = ""; }; + 66390723572B01E4BFAD632E /* Pods-Runner.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-internal.xcconfig"; sourceTree = ""; }; + 6B56F69AB969AA03109DA32E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 787ABA0FE3B824874C8F17D8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 949DAC436C6D6BCA06DF2227 /* Pods-Runner.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-production.xcconfig"; sourceTree = ""; }; + 8B9D10731A69F7B3B5726C07 /* Pods-Runner.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-production.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -55,8 +54,9 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9DAD5C3635B945C6034033E7 /* Pods-Runner.profile-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-internal.xcconfig"; sourceTree = ""; }; - CB4B1CD3242757983B2C4FFF /* Pods-Runner.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-production.xcconfig"; sourceTree = ""; }; + B275039F2F44BBC2812C49D6 /* Pods-Runner.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-production.xcconfig"; sourceTree = ""; }; + C87ABC9AD580EC1F17A8A628 /* Pods-Runner.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-production.xcconfig"; sourceTree = ""; }; + E6BE4CB718F715BF8881756E /* Pods-Runner.debug-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-internal.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -64,7 +64,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DC20FE362923283F45816BFC /* Pods_Runner.framework in Frameworks */, + 1B5DEA3E6EDC27B89AF2AB05 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -74,16 +74,24 @@ 337F646810BF58BC9EACB5E5 /* Pods */ = { isa = PBXGroup; children = ( - 502CCCDE3EE7AF774B525040 /* Pods-Runner.debug-production.xcconfig */, - 3BC12F23EEF9AAEBD6DC9470 /* Pods-Runner.debug-internal.xcconfig */, - 949DAC436C6D6BCA06DF2227 /* Pods-Runner.release-production.xcconfig */, - 6CC9F567B7A0A0781351BF49 /* Pods-Runner.release-internal.xcconfig */, - CB4B1CD3242757983B2C4FFF /* Pods-Runner.profile-production.xcconfig */, - 9DAD5C3635B945C6034033E7 /* Pods-Runner.profile-internal.xcconfig */, + 8B9D10731A69F7B3B5726C07 /* Pods-Runner.debug-production.xcconfig */, + E6BE4CB718F715BF8881756E /* Pods-Runner.debug-internal.xcconfig */, + B275039F2F44BBC2812C49D6 /* Pods-Runner.release-production.xcconfig */, + 66390723572B01E4BFAD632E /* Pods-Runner.release-internal.xcconfig */, + C87ABC9AD580EC1F17A8A628 /* Pods-Runner.profile-production.xcconfig */, + 5830A10298A0E37E0DEACD59 /* Pods-Runner.profile-internal.xcconfig */, ); path = Pods; sourceTree = ""; }; + 7B93B28FA9C10A6CB6A5BA08 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6B56F69AB969AA03109DA32E /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -106,7 +114,7 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 337F646810BF58BC9EACB5E5 /* Pods */, - 982F7AB9DEC126C4156BE443 /* Frameworks */, + 7B93B28FA9C10A6CB6A5BA08 /* Frameworks */, ); sourceTree = ""; }; @@ -135,14 +143,6 @@ path = Runner; sourceTree = ""; }; - 982F7AB9DEC126C4156BE443 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 787ABA0FE3B824874C8F17D8 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -150,14 +150,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 22CDF3ADC8F132E392D0F7D4 /* [CP] Check Pods Manifest.lock */, + 36CAFCEF69924F6DF3D8A5D2 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 6F66D87ECCC3A46691E42A5B /* [CP] Embed Pods Frameworks */, + C3DF71E2A734F75A9F432062 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -217,7 +217,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 22CDF3ADC8F132E392D0F7D4 /* [CP] Check Pods Manifest.lock */ = { + 36CAFCEF69924F6DF3D8A5D2 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -255,37 +255,37 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; - 6F66D87ECCC3A46691E42A5B /* [CP] Embed Pods Frameworks */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + inputPaths = ( ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + name = "Run Script"; + outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + C3DF71E2A734F75A9F432062 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputPaths = ( + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "Run Script"; - outputPaths = ( + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -548,7 +548,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2; @@ -567,7 +567,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.flutterdapp.internal; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp.internal"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.walletconnect.flutterdapp.internal"; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -632,7 +632,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; @@ -648,7 +648,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.flutterdapp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1724090152"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.walletconnect.flutterdapp"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -817,7 +817,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.flutterdapp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1724090152"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1727257949"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index c22e52a..bfa659c 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -124,11 +124,38 @@ class _MyHomePageState extends State { _appKit!.onSessionConnect.subscribe(_onSessionConnect); _appKit!.onSessionAuthResponse.subscribe(_onSessionAuthResponse); + // See https://docs.reown.com/appkit/flutter/core/custom-chains + final testNetworks = ReownAppKitModalNetworks.test['eip155'] ?? []; + ReownAppKitModalNetworks.addNetworks('eip155', testNetworks); + _appKitModal = ReownAppKitModal( context: context, appKit: _appKit, siweConfig: _siweConfig(), + enableAnalytics: true, enableEmail: true, + socials: [ + AppKitSocialOption.Apple, + AppKitSocialOption.Discord, + AppKitSocialOption.X, + AppKitSocialOption.Facebook, + AppKitSocialOption.Github, + ], + // requiredNamespaces: {}, + // optionalNamespaces: {}, + // includedWalletIds: {}, + featuredWalletIds: { + 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase + '18450873727504ae9315a084fa7624b5297d2fe5880f0982979c17345a138277', // Kraken Wallet + 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96', // Metamask + '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369', // Rainbow + 'c03dfee351b6fcc421b4494ea33b9d4b92a984f87aa76d1663bb28705e95034a', // Uniswap + '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662', // Bitget + }, + // excludedWalletIds: { + // 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', // Coinbase + // }, + // MORE WALLETS https://explorer.walletconnect.com/?type=wallet&chains=eip155%3A1 ); _appKitModal!.onModalConnect.subscribe(_onModalConnect); @@ -162,12 +189,12 @@ class _MyHomePageState extends State { icon: Icons.home, ), PageData( - page: PairingsPage(appKit: _appKit!), + page: PairingsPage(appKitModal: _appKitModal!), title: StringConstants.pairingsPageTitle, icon: Icons.vertical_align_center_rounded, ), PageData( - page: SessionsPage(appKit: _appKit!), + page: SessionsPage(appKitModal: _appKitModal!), title: StringConstants.sessionsPageTitle, icon: Icons.workspaces_filled, ), @@ -462,11 +489,12 @@ class _MyHomePageState extends State { ); void _onModalConnect(ModalConnect? event) async { - setState(() {}); debugPrint('[ExampleApp] _onModalConnect ${event?.session.toJson()}'); + setState(() {}); } void _onModalUpdate(ModalConnect? event) { + debugPrint('[ExampleApp] _onModalUpdate ${event?.session.toJson()}'); setState(() {}); } diff --git a/packages/reown_appkit/example/base/lib/pages/connect_page.dart b/packages/reown_appkit/example/base/lib/pages/connect_page.dart index c7dff84..7090342 100644 --- a/packages/reown_appkit/example/base/lib/pages/connect_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/connect_page.dart @@ -16,6 +16,7 @@ import 'package:reown_appkit_dapp/utils/crypto/solana.dart'; import 'package:reown_appkit_dapp/utils/sample_wallets.dart'; import 'package:reown_appkit_dapp/utils/string_constants.dart'; import 'package:reown_appkit_dapp/widgets/chain_button.dart'; +import 'package:reown_appkit_dapp/widgets/method_dialog.dart'; class ConnectPage extends StatefulWidget { const ConnectPage({ @@ -36,6 +37,12 @@ class ConnectPageState extends State { @override void initState() { super.initState(); + widget.appKitModal.onModalConnect.subscribe(_onModalConnect); + widget.appKitModal.onModalUpdate.subscribe(_onModalUpdate); + widget.appKitModal.onModalNetworkChange.subscribe(_onModalNetworkChange); + widget.appKitModal.onModalDisconnect.subscribe(_onModalDisconnect); + widget.appKitModal.onModalError.subscribe(_onModalError); + // widget.appKitModal.appKit!.onSessionConnect.subscribe( _onSessionConnect, ); @@ -49,6 +56,11 @@ class ConnectPageState extends State { @override void dispose() { + widget.appKitModal.onModalConnect.unsubscribe(_onModalConnect); + widget.appKitModal.onModalUpdate.unsubscribe(_onModalUpdate); + widget.appKitModal.onModalNetworkChange.unsubscribe(_onModalNetworkChange); + widget.appKitModal.onModalDisconnect.unsubscribe(_onModalDisconnect); + widget.appKitModal.onModalError.unsubscribe(_onModalError); widget.appKitModal.onModalDisconnect.unsubscribe( _onModalDisconnect, ); @@ -192,8 +204,14 @@ class ConnectPageState extends State { const SizedBox(height: StyleConstants.linear8), Visibility( visible: widget.appKitModal.isConnected, - child: AppKitModalAccountButton( - appKit: widget.appKitModal, + child: Column( + children: [ + AppKitModalAccountButton( + appKit: widget.appKitModal, + ), + const SizedBox.square(dimension: 8.0), + ...(_buildRequestButtons()), + ], ), ), const SizedBox(height: StyleConstants.linear8), @@ -408,6 +426,38 @@ class ConnectPageState extends State { ); } + List _buildRequestButtons() { + return widget.appKitModal.getApprovedMethods()?.map((method) { + final topic = widget.appKitModal.session!.topic ?? ''; + final chainId = widget.appKitModal.selectedChain!.chainId; + final address = widget.appKitModal.session!.address!; + final requestParams = EIP155.getParams(method, address); + final enabled = requestParams != null; + return Container( + height: 40.0, + width: double.infinity, + margin: const EdgeInsets.symmetric( + vertical: StyleConstants.linear8, + ), + child: ElevatedButton( + onPressed: enabled + ? () { + widget.appKitModal.launchConnectedWallet(); + final future = widget.appKitModal.request( + topic: topic, + chainId: chainId, + request: requestParams, + ); + MethodDialog.show(context, method, future); + } + : null, + child: Text(method), + ), + ); + }).toList() ?? + []; + } + Future _onConnect({ required String nativeLink, VoidCallback? closeModal, @@ -559,10 +609,26 @@ class ConnectPageState extends State { } } + void _onModalConnect(ModalConnect? event) async { + setState(() {}); + } + + void _onModalUpdate(ModalConnect? event) { + setState(() {}); + } + + void _onModalNetworkChange(ModalNetworkChange? event) { + setState(() {}); + } + void _onModalDisconnect(ModalDisconnect? event) { setState(() {}); } + void _onModalError(ModalError? event) { + setState(() {}); + } + ButtonStyle get _buttonStyle => ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (states) { diff --git a/packages/reown_appkit/example/base/lib/pages/pairings_page.dart b/packages/reown_appkit/example/base/lib/pages/pairings_page.dart index a3db3e0..46f0517 100644 --- a/packages/reown_appkit/example/base/lib/pages/pairings_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/pairings_page.dart @@ -8,10 +8,10 @@ import 'package:reown_appkit_dapp/widgets/pairing_item.dart'; class PairingsPage extends StatefulWidget { const PairingsPage({ super.key, - required this.appKit, + required this.appKitModal, }); - final ReownAppKit appKit; + final ReownAppKitModal appKitModal; @override PairingsPageState createState() => PairingsPageState(); @@ -19,26 +19,31 @@ class PairingsPage extends StatefulWidget { class PairingsPageState extends State { List _pairings = []; + late IReownAppKit _appKit; @override void initState() { - _pairings = widget.appKit.pairings.getAll(); - // widget.appKit.onSessionDelete.subscribe(_onSessionDelete); - widget.appKit.core.pairing.onPairingDelete.subscribe(_onPairingDelete); - widget.appKit.core.pairing.onPairingExpire.subscribe(_onPairingDelete); + _appKit = widget.appKitModal.appKit!; + _pairings = _appKit.pairings.getAll(); + _appKit.core.pairing.onPairingDelete.subscribe(_onPairingDelete); + _appKit.core.pairing.onPairingExpire.subscribe(_onPairingDelete); super.initState(); } @override void dispose() { - // widget.appKit.onSessionDelete.unsubscribe(_onSessionDelete); - widget.appKit.core.pairing.onPairingDelete.unsubscribe(_onPairingDelete); - widget.appKit.core.pairing.onPairingExpire.unsubscribe(_onPairingDelete); + _appKit.core.pairing.onPairingDelete.unsubscribe(_onPairingDelete); + _appKit.core.pairing.onPairingExpire.unsubscribe(_onPairingDelete); super.dispose(); } @override Widget build(BuildContext context) { + if (_pairings.isEmpty) { + return Center( + child: Text('No relay pairings'), + ); + } final List pairingItems = _pairings .map( (PairingInfo pairing) => PairingItem( @@ -71,7 +76,7 @@ class PairingsPageState extends State { ), onPressed: () async { try { - widget.appKit.core.pairing.disconnect( + _appKit.core.pairing.disconnect( topic: pairing.topic, ); Navigator.of(context).pop(); @@ -104,7 +109,7 @@ class PairingsPageState extends State { void _onPairingDelete(PairingEvent? event) { setState(() { - _pairings = widget.appKit.pairings.getAll(); + _pairings = _appKit.pairings.getAll(); }); } } diff --git a/packages/reown_appkit/example/base/lib/pages/sessions_page.dart b/packages/reown_appkit/example/base/lib/pages/sessions_page.dart index 3b23b9d..412e318 100644 --- a/packages/reown_appkit/example/base/lib/pages/sessions_page.dart +++ b/packages/reown_appkit/example/base/lib/pages/sessions_page.dart @@ -9,37 +9,44 @@ import 'package:reown_appkit_dapp/widgets/session_widget.dart'; class SessionsPage extends StatefulWidget { const SessionsPage({ super.key, - required this.appKit, + required this.appKitModal, }); - final ReownAppKit appKit; + final ReownAppKitModal appKitModal; @override SessionsPageState createState() => SessionsPageState(); } class SessionsPageState extends State { + late IReownAppKit _appKit; Map _activeSessions = {}; - String _selectedSession = ''; + String _selectedTopic = ''; @override void initState() { - _activeSessions = widget.appKit.getActiveSessions(); - widget.appKit.onSessionDelete.subscribe(_onSessionDelete); - widget.appKit.onSessionExpire.subscribe(_onSessionExpire); + _appKit = widget.appKitModal.appKit!; + _activeSessions = _appKit.getActiveSessions(); + _appKit.onSessionDelete.subscribe(_onSessionDelete); + _appKit.onSessionExpire.subscribe(_onSessionExpire); super.initState(); } @override void dispose() { - widget.appKit.onSessionDelete.unsubscribe(_onSessionDelete); - widget.appKit.onSessionExpire.unsubscribe(_onSessionExpire); + _appKit.onSessionDelete.unsubscribe(_onSessionDelete); + _appKit.onSessionExpire.unsubscribe(_onSessionExpire); super.dispose(); } @override Widget build(BuildContext context) { final List sessions = _activeSessions.values.toList(); + if (sessions.isEmpty) { + return Center( + child: Text('No relay sessions'), + ); + } return Center( child: Container( constraints: const BoxConstraints( @@ -53,14 +60,14 @@ class SessionsPageState extends State { materialGapSize: 0.0, expansionCallback: (int index, bool isExpanded) { setState(() { - _selectedSession = !isExpanded ? '' : sessions[index].topic; + _selectedTopic = !isExpanded ? '' : sessions[index].topic; }); }, children: sessions .map( (session) => ExpansionPanel( canTapOnHeader: true, - isExpanded: _selectedSession == session.topic, + isExpanded: _selectedTopic == session.topic, backgroundColor: Colors.blue.withOpacity(0.2), headerBuilder: (context, isExpanded) { return SessionItem( @@ -84,7 +91,7 @@ class SessionsPageState extends State { } Widget _buildSessionView() { - if (_selectedSession == '') { + if (_selectedTopic == '') { return const Center( child: Text( StringConstants.noSessionSelected, @@ -93,29 +100,27 @@ class SessionsPageState extends State { ); } - final SessionData session = _activeSessions[_selectedSession]!; - return SessionWidget( - appKit: widget.appKit, - session: session, + appKitModal: widget.appKitModal, + sessionTopic: _selectedTopic, ); } void _onSessionDelete(SessionDelete? event) { setState(() { - if (event!.topic == _selectedSession) { - _selectedSession = ''; + if (event!.topic == _selectedTopic) { + _selectedTopic = ''; } - _activeSessions = widget.appKit.getActiveSessions(); + _activeSessions = _appKit.getActiveSessions(); }); } void _onSessionExpire(SessionExpire? event) { setState(() { - if (event!.topic == _selectedSession) { - _selectedSession = ''; + if (event!.topic == _selectedTopic) { + _selectedTopic = ''; } - _activeSessions = widget.appKit.getActiveSessions(); + _activeSessions = _appKit.getActiveSessions(); }); } } diff --git a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart index aa8a441..5cfde85 100644 --- a/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart +++ b/packages/reown_appkit/example/base/lib/utils/crypto/eip155.dart @@ -36,7 +36,7 @@ class EIP155 { }; static Future callMethod({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String method, required ChainMetadata chainData, @@ -49,7 +49,6 @@ class EIP155 { topic: topic, chainId: chainData.chainId, address: address, - message: testSignData, ); case 'eth_sign': return ethSign( @@ -57,7 +56,6 @@ class EIP155 { topic: topic, chainId: chainData.chainId, address: address, - message: testSignData, ); case 'eth_signTypedData': return ethSignTypedData( @@ -65,33 +63,18 @@ class EIP155 { topic: topic, chainId: chainData.chainId, address: address, - data: typedData, ); case 'eth_signTransaction': return ethSignTransaction( appKit: appKit, topic: topic, chainId: chainData.chainId, - transaction: Transaction( - from: EthereumAddress.fromHex(address), - to: EthereumAddress.fromHex( - '0x59e2f66C0E96803206B6486cDb39029abAE834c0', - ), - value: EtherAmount.fromInt(EtherUnit.finney, 12), // == 0.012 - ), ); case 'eth_sendTransaction': return ethSendTransaction( appKit: appKit, topic: topic, chainId: chainData.chainId, - transaction: Transaction( - from: EthereumAddress.fromHex(address), - to: EthereumAddress.fromHex( - '0x59e2f66C0E96803206B6486cDb39029abAE834c0', - ), - value: EtherAmount.fromInt(EtherUnit.finney, 11), // == 0.011 - ), ); default: throw 'Method unimplemented'; @@ -99,7 +82,7 @@ class EIP155 { } static Future callSmartContract({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String address, required String action, @@ -148,93 +131,70 @@ class EIP155 { } static Future personalSign({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String chainId, required String address, - required String message, }) async { - final bytes = utf8.encode(message); - final encoded = bytesToHex(bytes, include0x: true); - return await appKit.request( topic: topic, chainId: chainId, - request: SessionRequestParams( - method: methods[EIP155Methods.personalSign]!, - params: [encoded, address], - ), + request: getParams('personal_sign', address)!, ); } static Future ethSign({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String chainId, required String address, - required String message, }) async { return await appKit.request( topic: topic, chainId: chainId, - request: SessionRequestParams( - method: methods[EIP155Methods.ethSign]!, - params: [address, message], - ), + request: getParams('eth_sign', address)!, ); } static Future ethSignTypedData({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String chainId, required String address, - required String data, }) async { return await appKit.request( topic: topic, chainId: chainId, - request: SessionRequestParams( - method: methods[EIP155Methods.ethSignTypedData]!, - params: [address, data], - ), + request: getParams('eth_signTypedData', address)!, ); } static Future ethSignTransaction({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String chainId, - required Transaction transaction, }) async { return await appKit.request( topic: topic, chainId: chainId, - request: SessionRequestParams( - method: methods[EIP155Methods.ethSignTransaction]!, - params: [transaction.toJson()], - ), + request: getParams('eth_signTransaction', '')!, ); } static Future ethSendTransaction({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String topic, required String chainId, - required Transaction transaction, }) async { return await appKit.request( topic: topic, chainId: chainId, - request: SessionRequestParams( - method: methods[EIP155Methods.ethSendTransaction]!, - params: [transaction.toJson()], - ), + request: getParams('eth_sendTransaction', '')!, ); } static Future readSmartContract({ - required ReownAppKit appKit, + required IReownAppKit appKit, required String rpcUrl, required String address, required DeployedContract contract, @@ -274,4 +234,54 @@ class EIP155 { 'balance': oCcy.format(balance), }; } + + static SessionRequestParams? getParams(String method, String address) { + switch (method) { + case 'personal_sign': + final bytes = utf8.encode(testSignData); + final encoded = bytesToHex(bytes, include0x: true); + return SessionRequestParams( + method: methods[EIP155Methods.personalSign]!, + params: [encoded, address], + ); + case 'eth_sign': + return SessionRequestParams( + method: methods[EIP155Methods.ethSign]!, + params: [address, testSignData], + ); + case 'eth_signTypedData': + return SessionRequestParams( + method: methods[EIP155Methods.ethSignTypedData]!, + params: [address, typedData], + ); + case 'eth_signTransaction': + return SessionRequestParams( + method: methods[EIP155Methods.ethSignTransaction]!, + params: [ + Transaction( + from: EthereumAddress.fromHex(address), + to: EthereumAddress.fromHex( + '0x59e2f66C0E96803206B6486cDb39029abAE834c0', + ), + value: EtherAmount.fromInt(EtherUnit.finney, 12), // == 0.012 + ).toJson(), + ], + ); + case 'eth_sendTransaction': + return SessionRequestParams( + method: methods[EIP155Methods.ethSendTransaction]!, + params: [ + Transaction( + from: EthereumAddress.fromHex(address), + to: EthereumAddress.fromHex( + '0x59e2f66C0E96803206B6486cDb39029abAE834c0', + ), + value: EtherAmount.fromInt(EtherUnit.finney, 12), // == 0.012 + ).toJson(), + ], + ); + default: + return null; + } + } } diff --git a/packages/reown_appkit/example/base/lib/widgets/session_widget.dart b/packages/reown_appkit/example/base/lib/widgets/session_widget.dart index 93e0989..f98e16f 100644 --- a/packages/reown_appkit/example/base/lib/widgets/session_widget.dart +++ b/packages/reown_appkit/example/base/lib/widgets/session_widget.dart @@ -14,23 +14,33 @@ import 'package:reown_appkit_dapp/widgets/method_dialog.dart'; class SessionWidget extends StatefulWidget { const SessionWidget({ super.key, - required this.session, - required this.appKit, + required this.sessionTopic, + required this.appKitModal, }); - final SessionData session; - final ReownAppKit appKit; + final String sessionTopic; + final ReownAppKitModal appKitModal; @override SessionWidgetState createState() => SessionWidgetState(); } class SessionWidgetState extends State { + late IReownAppKit _appKit; + late SessionData _session; + + @override + void initState() { + super.initState(); + _appKit = widget.appKitModal.appKit!; + _session = _appKit.sessions.get(widget.sessionTopic)!; + } + @override Widget build(BuildContext context) { final List children = [ Text( - '${StringConstants.sessionTopic}${widget.session.topic}', + '${StringConstants.sessionTopic}${_session.topic}', ), ]; @@ -38,7 +48,7 @@ class SessionWidgetState extends State { final List namespaceAccounts = []; // Loop through the namespaces, and get the accounts - for (final Namespace namespace in widget.session.namespaces.values) { + for (final Namespace namespace in _session.namespaces.values) { namespaceAccounts.addAll(namespace.accounts); } @@ -61,8 +71,8 @@ class SessionWidgetState extends State { ), child: ElevatedButton( onPressed: () async { - await widget.appKit.disconnectSession( - topic: widget.session.topic, + await _appKit.disconnectSession( + topic: _session.topic, reason: Errors.getSdkError( Errors.USER_DISCONNECTED, ).toSignError(), @@ -170,7 +180,7 @@ class SessionWidgetState extends State { final List buttons = []; // Add Methods for (final String method in getChainMethods(chainMetadata.type)) { - final namespaces = widget.session.namespaces[chainMetadata.type.name]; + final namespaces = _session.namespaces[chainMetadata.type.name]; final supported = namespaces?.methods.contains(method) ?? false; buttons.add( Container( @@ -226,24 +236,24 @@ class SessionWidgetState extends State { switch (chainMetadata.type) { case ChainType.eip155: return EIP155.callMethod( - appKit: widget.appKit, - topic: widget.session.topic, + appKit: _appKit, + topic: _session.topic, method: method, chainData: chainMetadata, address: address, ); case ChainType.polkadot: return Polkadot.callMethod( - appKit: widget.appKit, - topic: widget.session.topic, + appKit: _appKit, + topic: _session.topic, method: method, chainId: chainMetadata.chainId, address: address, ); case ChainType.solana: return Solana.callMethod( - appKit: widget.appKit, - topic: widget.session.topic, + appKit: _appKit, + topic: _session.topic, method: method, chainData: chainMetadata, address: address, @@ -251,8 +261,8 @@ class SessionWidgetState extends State { ); // case ChainType.kadena: // return Kadena.callMethod( - // appKit: widget.appKit, - // topic: widget.session.topic, + // appKit: _appKit, + // topic: _session.topic, // method: method.toKadenaMethod()!, // chainId: chainMetadata.chainId, // address: address.toLowerCase(), @@ -264,9 +274,9 @@ class SessionWidgetState extends State { void _launchWallet() { if (kIsWeb) return; - widget.appKit.redirectToWallet( - topic: widget.session.topic, - redirect: widget.session.peer.metadata.redirect, + _appKit.redirectToWallet( + topic: _session.topic, + redirect: _session.peer.metadata.redirect, ); } @@ -284,8 +294,8 @@ class SessionWidgetState extends State { onPressed: enabled ? () async { final future = EIP155.callSmartContract( - appKit: widget.appKit, - topic: widget.session.topic, + appKit: _appKit, + topic: _session.topic, address: address, action: 'read', ); @@ -326,8 +336,8 @@ class SessionWidgetState extends State { onPressed: enabled ? () async { final future = EIP155.callSmartContract( - appKit: widget.appKit, - topic: widget.session.topic, + appKit: _appKit, + topic: _session.topic, address: address, action: 'write', ); diff --git a/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj b/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj index cb7df21..b58df8d 100644 --- a/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj @@ -9,10 +9,10 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; - 39AD9E7C47D2C2E726BC1CD1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67AE57406F4CAFF18576119B /* Pods_RunnerTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 516D0608705F0BE97FC929E4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D119754EA0AC5CBD4B950C40 /* Pods_Runner.framework */; }; + 6561844AD097F2CFC6B48E58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F80C0F0A2865C918627E1584 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 8FA6AEDFDC4C361E30EC02B5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6989162A0A87467687F5F26F /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -45,13 +45,11 @@ 092D151B2ABD988600C69848 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 16888CA3F81E677DDEC3A0CD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 26C1413BE27E5DC446084413 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 362D7224BA747ECF2E28DB1F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 67AE57406F4CAFF18576119B /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6989162A0A87467687F5F26F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 61E23A67BDA67FF5359D9357 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -62,10 +60,12 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B2930B7741FEE25EBE53F9E1 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - D06C3C4B2781C786140748A0 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - E9367D8F7B7044CEE6C79C75 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - FBB093E541A895EE125D344E /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + A169B40BA5CA19BC0389255C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + B5705635CD688D31EE6A40E6 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + BB3CF3338CE662C91545CB38 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + CECA027B1D7BD08002F6D591 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + D119754EA0AC5CBD4B950C40 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F80C0F0A2865C918627E1584 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -73,7 +73,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 39AD9E7C47D2C2E726BC1CD1 /* Pods_RunnerTests.framework in Frameworks */, + 6561844AD097F2CFC6B48E58 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -81,22 +81,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8FA6AEDFDC4C361E30EC02B5 /* Pods_Runner.framework in Frameworks */, + 516D0608705F0BE97FC929E4 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1FB98289B12D49950E73D65F /* Frameworks */ = { - isa = PBXGroup; - children = ( - 6989162A0A87467687F5F26F /* Pods_Runner.framework */, - 67AE57406F4CAFF18576119B /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -124,7 +115,7 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, E880F334C300E1CEFAFD6E4C /* Pods */, - 1FB98289B12D49950E73D65F /* Frameworks */, + C37C162CA8492E9BE105CA16 /* Frameworks */, ); sourceTree = ""; }; @@ -153,15 +144,24 @@ path = Runner; sourceTree = ""; }; + C37C162CA8492E9BE105CA16 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D119754EA0AC5CBD4B950C40 /* Pods_Runner.framework */, + F80C0F0A2865C918627E1584 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; E880F334C300E1CEFAFD6E4C /* Pods */ = { isa = PBXGroup; children = ( - 16888CA3F81E677DDEC3A0CD /* Pods-Runner.debug.xcconfig */, - E9367D8F7B7044CEE6C79C75 /* Pods-Runner.release.xcconfig */, - B2930B7741FEE25EBE53F9E1 /* Pods-Runner.profile.xcconfig */, - D06C3C4B2781C786140748A0 /* Pods-RunnerTests.debug.xcconfig */, - 362D7224BA747ECF2E28DB1F /* Pods-RunnerTests.release.xcconfig */, - FBB093E541A895EE125D344E /* Pods-RunnerTests.profile.xcconfig */, + 61E23A67BDA67FF5359D9357 /* Pods-Runner.debug.xcconfig */, + A169B40BA5CA19BC0389255C /* Pods-Runner.release.xcconfig */, + BB3CF3338CE662C91545CB38 /* Pods-Runner.profile.xcconfig */, + CECA027B1D7BD08002F6D591 /* Pods-RunnerTests.debug.xcconfig */, + B5705635CD688D31EE6A40E6 /* Pods-RunnerTests.release.xcconfig */, + 26C1413BE27E5DC446084413 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -173,7 +173,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - A651C144B5361BC3EF29C3BB /* [CP] Check Pods Manifest.lock */, + 70C2F9C0A5F8998280E23BFC /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 6D09732E2619FACF7894A4A1 /* Frameworks */, @@ -192,14 +192,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 19E33495D5B1E24E7C81DB51 /* [CP] Check Pods Manifest.lock */, + 06D78A8D11118782BCBF4EA7 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - A8353A6875DADDB86DC82CE5 /* [CP] Embed Pods Frameworks */, + BD4CB75C11799DFB8AB18650 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -270,7 +270,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 19E33495D5B1E24E7C81DB51 /* [CP] Check Pods Manifest.lock */ = { + 06D78A8D11118782BCBF4EA7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -308,44 +308,44 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 70C2F9C0A5F8998280E23BFC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Run Script"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - A651C144B5361BC3EF29C3BB /* [CP] Check Pods Manifest.lock */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( ); + name = "Run Script"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - A8353A6875DADDB86DC82CE5 /* [CP] Embed Pods Frameworks */ = { + BD4CB75C11799DFB8AB18650 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -469,6 +469,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = ""; @@ -483,7 +484,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.web3modal.flutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = FlutterDevelopmentProfile2; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.web3modal.flutterExample"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -492,7 +493,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D06C3C4B2781C786140748A0 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = CECA027B1D7BD08002F6D591 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -510,7 +511,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 362D7224BA747ECF2E28DB1F /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = B5705635CD688D31EE6A40E6 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -526,7 +527,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FBB093E541A895EE125D344E /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 26C1413BE27E5DC446084413 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -655,9 +656,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 73; - DEVELOPMENT_TEAM = W5R8AG9K22; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -668,6 +671,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.web3modal.flutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.web3modal.flutterExample"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -698,7 +702,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.web3modal.flutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = FlutterAppStoreProfileWithPush; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.web3modal.flutterExample"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift b/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift index 81aa14a..242bf15 100644 --- a/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift +++ b/packages/reown_appkit/example/modal/ios/Runner/AppDelegate.swift @@ -4,11 +4,47 @@ import CoinbaseWalletSDK @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { + + private static let EVENTS_CHANNEL = "com.web3modal.flutterExample/events" + private static let METHODS_CHANNEL = "com.web3modal.flutterExample/methods" + + private var eventsChannel: FlutterEventChannel? + private var methodsChannel: FlutterMethodChannel? + var initialLink: String? + + private let linkStreamHandler = LinkStreamHandler() + + override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GeneratedPluginRegistrant.register(with: self) + + let controller = window.rootViewController as! FlutterViewController + eventsChannel = FlutterEventChannel(name: AppDelegate.EVENTS_CHANNEL, binaryMessenger: controller.binaryMessenger) + eventsChannel?.setStreamHandler(linkStreamHandler) + + methodsChannel = FlutterMethodChannel(name: AppDelegate.METHODS_CHANNEL, binaryMessenger: controller.binaryMessenger) + methodsChannel?.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in + if (call.method == "initialLink") { + if let link = self?.initialLink { + let handled = self?.linkStreamHandler.handleLink(link) + if (handled == true) { + self?.initialLink = nil + } + } + } + }) + + // Add your deep link handling logic here + if let url = launchOptions?[.url] as? URL { + self.initialLink = url.absoluteString + } + + if let userActivityDictionary = launchOptions?[.userActivityDictionary] as? [String: Any], + let userActivity = userActivityDictionary["UIApplicationLaunchOptionsUserActivityKey"] as? NSUserActivity, + userActivity.activityType == NSUserActivityTypeBrowsingWeb { + + handleIncomingUniversalLink(userActivity: userActivity) + } + return super.application(application, didFinishLaunchingWithOptions: launchOptions) } @@ -21,19 +57,60 @@ import CoinbaseWalletSDK } } - return super.application(app, open: url, options: options) + return linkStreamHandler.handleLink(url.absoluteString) } override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if #available(iOS 13.0, *) { if (CoinbaseWalletSDK.isConfigured == true) { - if let url = userActivity.webpageURL, - (try? CoinbaseWalletSDK.shared.handleResponse(url)) == true { + if let url = userActivity.webpageURL, (try? CoinbaseWalletSDK.shared.handleResponse(url)) == true { return true } } } - return super.application(application, continue: userActivity, restorationHandler: restorationHandler) + if userActivity.activityType == NSUserActivityTypeBrowsingWeb { + handleIncomingUniversalLink(userActivity: userActivity) + return true + } + + return false + } + + private func handleIncomingUniversalLink(userActivity: NSUserActivity) { + if let url = userActivity.webpageURL { + // Handle the URL, navigate to appropriate screen + print("App launched with Universal Link: \(url.absoluteString)") + let handled = linkStreamHandler.handleLink(url.absoluteString) + if (!handled){ + self.initialLink = url.absoluteString + } + } } } + +class LinkStreamHandler: NSObject, FlutterStreamHandler { + var eventSink: FlutterEventSink? + var queuedLinks = [String]() + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + self.eventSink = events + queuedLinks.forEach({ events($0) }) + queuedLinks.removeAll() + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + self.eventSink = nil + return nil + } + + func handleLink(_ link: String) -> Bool { + guard let eventSink = eventSink else { + queuedLinks.append(link) + return false + } + eventSink(link) + return true + } +} \ No newline at end of file diff --git a/packages/reown_appkit/example/modal/ios/Runner/Runner.entitlements b/packages/reown_appkit/example/modal/ios/Runner/Runner.entitlements index 903def2..bfe855c 100644 --- a/packages/reown_appkit/example/modal/ios/Runner/Runner.entitlements +++ b/packages/reown_appkit/example/modal/ios/Runner/Runner.entitlements @@ -2,7 +2,9 @@ - aps-environment - development + com.apple.developer.associated-domains + + applinks:appkit-lab.reown.com + diff --git a/packages/reown_appkit/example/modal/lib/home_page.dart b/packages/reown_appkit/example/modal/lib/home_page.dart index 2c9cad1..662227d 100644 --- a/packages/reown_appkit/example/modal/lib/home_page.dart +++ b/packages/reown_appkit/example/modal/lib/home_page.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:fl_toast/fl_toast.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:reown_appkit_example/services/deep_link_handler.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:reown_appkit/reown_appkit.dart'; @@ -64,9 +65,8 @@ class _MyHomePageState extends State { } String _universalLink() { - // TODO change /flutter_appkit to something else - Uri link = Uri.parse('https://appkit-lab.reown.com/flutter_appkit'); - if (_flavor.isNotEmpty) { + Uri link = Uri.parse('https://appkit-lab.reown.com/flutter_appkit_modal'); + if (_flavor.isNotEmpty && !kDebugMode) { return link.replace(path: '${link.path}_internal').toString(); } return link.toString(); @@ -220,15 +220,13 @@ class _MyHomePageState extends State { enableAnalytics: analyticsValue, // OPTIONAL - null by default enableEmail: emailWalletValue, // OPTIONAL - false by default socials: [ - AppKitSocialOption.Google, + // AppKitSocialOption.Google, AppKitSocialOption.Apple, AppKitSocialOption.Discord, - AppKitSocialOption.Github, AppKitSocialOption.X, AppKitSocialOption.Facebook, + AppKitSocialOption.Github, AppKitSocialOption.Farcaster, - AppKitSocialOption.Twitch, - AppKitSocialOption.Telegram, ], // requiredNamespaces: {}, // optionalNamespaces: {}, @@ -274,6 +272,10 @@ class _MyHomePageState extends State { _appKitModal.appKit!.core.addLogListener(_logListener); // await _appKitModal.init(); + + DeepLinkHandler.init(_appKitModal); + DeepLinkHandler.checkInitialLink(); + setState(() {}); } @@ -378,6 +380,9 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { + if (!_initialized) { + return SizedBox.shrink(); + } return Scaffold( backgroundColor: ReownAppKitModalTheme.colorsOf(context).background125, appBar: AppBar( @@ -407,6 +412,7 @@ class _MyHomePageState extends State { toggleOverlay: _toggleOverlay, toggleBrightness: widget.toggleBrightness, toggleTheme: widget.toggleTheme, + appKitModal: _appKitModal, ), ), onEndDrawerChanged: (isOpen) { diff --git a/packages/reown_appkit/example/modal/lib/main.dart b/packages/reown_appkit/example/modal/lib/main.dart index f4d292d..7142b46 100644 --- a/packages/reown_appkit/example/modal/lib/main.dart +++ b/packages/reown_appkit/example/modal/lib/main.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; import 'package:reown_appkit_example/home_page.dart'; +import 'package:reown_appkit_example/services/deep_link_handler.dart'; import 'package:reown_appkit_example/utils/constants.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); + DeepLinkHandler.initListener(); runApp(const MyApp()); } diff --git a/packages/reown_appkit/example/modal/lib/services/deep_link_handler.dart b/packages/reown_appkit/example/modal/lib/services/deep_link_handler.dart new file mode 100644 index 0000000..941fe37 --- /dev/null +++ b/packages/reown_appkit/example/modal/lib/services/deep_link_handler.dart @@ -0,0 +1,57 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; + +class DeepLinkHandler { + static const _methodChannel = MethodChannel( + 'com.web3modal.flutterExample/methods', + ); + static const _eventChannel = EventChannel( + 'com.web3modal.flutterExample/events', + ); + static final waiting = ValueNotifier(false); + static late IReownAppKitModal _appKitModal; + + static void initListener() { + if (kIsWeb) return; + try { + _eventChannel.receiveBroadcastStream().listen( + _onLink, + onError: _onError, + ); + } catch (e) { + debugPrint('[SampleModal] initListener $e'); + } + } + + static void init(IReownAppKitModal appKitModal) { + if (kIsWeb) return; + _appKitModal = appKitModal; + } + + static void checkInitialLink() async { + if (kIsWeb) return; + try { + _methodChannel.invokeMethod('initialLink'); + } catch (e) { + debugPrint('[SampleModal] checkInitialLink $e'); + } + } + + static Uri get nativeUri => + Uri.parse(_appKitModal.appKit!.metadata.redirect?.native ?? ''); + static Uri get universalUri => + Uri.parse(_appKitModal.appKit!.metadata.redirect?.universal ?? ''); + static String get host => universalUri.host; + + static void _onLink(dynamic link) async { + debugPrint('[SampleModal] _onLink $link'); + if (link == null) return; + await _appKitModal.dispatchEnvelope(link); + } + + static void _onError(dynamic error) { + debugPrint('[SampleModal] _onError $error'); + waiting.value = false; + } +} diff --git a/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart b/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart index 4cc98f1..7db646e 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/debug_drawer.dart @@ -1,24 +1,32 @@ +// ignore_for_file: depend_on_referenced_packages + import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -// ignore: depend_on_referenced_packages + import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:reown_appkit/version.dart' as apkt; import 'package:reown_appkit/reown_appkit.dart'; +import 'package:reown_appkit/version.dart' as apkt_v; + +import 'package:reown_sign/version.dart' as sign_v; +import 'package:reown_core/version.dart' as core_v; + class DebugDrawer extends StatefulWidget { const DebugDrawer({ super.key, required this.toggleOverlay, required this.toggleBrightness, required this.toggleTheme, + required this.appKitModal, }); final VoidCallback toggleOverlay; final VoidCallback toggleBrightness; final VoidCallback toggleTheme; + final ReownAppKitModal appKitModal; @override State createState() => _DebugDrawerState(); @@ -234,32 +242,105 @@ class _DebugDrawerState extends State with WidgetsBindingObserver { ], ), ), - FutureBuilder( - future: PackageInfo.fromPlatform(), - builder: (context, snapshot) { - return InkWell( - onTap: () { - Clipboard.setData( - ClipboardData( - text: - '${snapshot.data?.packageName} v${snapshot.data?.version ?? ''} (${snapshot.data?.buildNumber})\n' - 'AppKit v${apkt.packageVersion}\n' - 'Core v$packageVersion', - ), - ); - }, - child: Text( - '${snapshot.data?.packageName} v${snapshot.data?.version ?? ''} (${snapshot.data?.buildNumber})\n' - 'AppKit v${apkt.packageVersion}\n' - 'Core v$packageVersion', + const SizedBox(height: 16.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Redirect:', + textAlign: TextAlign.left, style: TextStyle( fontSize: 12.0, - color: - ReownAppKitModalTheme.colorsOf(context).foreground100, + fontWeight: FontWeight.bold, ), ), - ); - }, + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Native: ', + style: TextStyle(fontSize: 12.0), + ), + Expanded( + child: Text( + '${widget.appKitModal.appKit!.metadata.redirect?.native}', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.bold, + color: ReownAppKitModalTheme.colorsOf(context) + .foreground100, + ), + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Universal: ', + style: TextStyle(fontSize: 12.0), + ), + Expanded( + child: Text( + '${widget.appKitModal.appKit!.metadata.redirect?.universal}', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.bold, + color: ReownAppKitModalTheme.colorsOf(context) + .foreground100, + ), + ), + ), + ], + ), + Row( + children: [ + const Text( + 'Link Mode: ', + style: TextStyle(fontSize: 12.0), + ), + Text( + '${widget.appKitModal.appKit!.metadata.redirect?.linkMode}', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.bold, + color: ReownAppKitModalTheme.colorsOf(context) + .foreground100, + ), + ), + ], + ), + const Divider(height: 10.0), + FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) { + final versionText = + '${snapshot.data?.packageName} v${snapshot.data?.version ?? ''} (${snapshot.data?.buildNumber})\n' + 'AppKit v${apkt_v.packageVersion}\n' + 'Sign v${sign_v.packageVersion}\n' + 'Core v${core_v.packageVersion}'; + return InkWell( + onTap: () => Clipboard.setData(ClipboardData( + text: versionText, + )), + child: Text( + versionText, + style: TextStyle( + fontSize: 12.0, + color: ReownAppKitModalTheme.colorsOf(context) + .foreground100, + ), + ), + ); + }, + ), + ], + ), ), const SizedBox.square(dimension: 10.0), const Divider(height: 1.0, indent: 12.0, endIndent: 12.0), diff --git a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart index ed8fb45..5537fcb 100644 --- a/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart +++ b/packages/reown_appkit/example/modal/lib/widgets/session_widget.dart @@ -165,15 +165,8 @@ class SessionWidgetState extends State { } Widget _buildAccountWidget(String account) { - // final chainId = NamespaceUtils.getChainFromAccount(account); - // final chainMetadata = ChainDataWrapper.getChainMetadataFromChain( - // chainId.split(':').first, - // chainId.split(':').last, - // ); - final List children = [ Text( - // chainMetadata.appKitNetworkInfo.name, widget.appKit.selectedChain?.name ?? 'Unsupported chain', style: ReownAppKitModalTheme.getDataOf(context) .textStyles @@ -271,12 +264,9 @@ class SessionWidgetState extends State { onPressed: implemented ? () async { widget.appKit.launchConnectedWallet(); - final future = callChainMethod( - // chainMetadata.type, - EIP155.methodFromName(method) - // chainMetadata, - // address, - ); + final future = callChainMethod(EIP155.methodFromName( + method, + )); MethodDialog.show(context, method, future); } : null, @@ -457,12 +447,7 @@ class SessionWidgetState extends State { ); } - Future callChainMethod( - // ChainType type, - EIP155UIMethods method, - // ChainMetadata chainMetadata, - // String address, - ) { + Future callChainMethod(EIP155UIMethods method) { final session = widget.appKit.session!; return EIP155.callMethod( appKit: widget.appKit, diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index 30a5a79..a0a70e6 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; -import 'dart:developer' as dev; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -201,7 +200,7 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { magicService.instance = MagicService( core: _appKit.core, metadata: _appKit.metadata, - enabled: enableEmail, + enableEmail: enableEmail, socials: socials, ); @@ -221,6 +220,14 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { bool _serviceInitialized = false; + @override + Future dispatchEnvelope(String url) async { + final envelope = ReownCoreUtils.getSearchParamFromURL(url, 'wc_ev'); + if (envelope.isNotEmpty) { + return await _appKit.dispatchEnvelope(url); + } + } + @override Future init() async { _serviceInitialized = false; @@ -282,7 +289,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // There's a session stored if (wcSessions.isNotEmpty) { await _storeSession( - ReownAppKitModalSession(sessionData: wcSessions.first)); + ReownAppKitModalSession( + sessionData: wcSessions.first, + ), + ); // session should not outlive the pairing if (wcPairings.isEmpty) { await disconnect(); @@ -302,6 +312,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { // So we will need to treat magic session differently final email = _currentSession!.email; magicService.instance.setEmail(email); + // TODO make it better + final provider = _currentSession!.toRawJson()['provider'] as String?; + magicService.instance.setProvider(provider); } else { await _cleanSession(); } @@ -341,11 +354,10 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { } } - Future _setSesionAndChainData( - ReownAppKitModalSession modalSession) async { + Future _setSesionAndChainData(ReownAppKitModalSession sData) async { try { - await _storeSession(modalSession); - _currentSelectedChainId = _currentSelectedChainId ?? modalSession.chainId; + await _storeSession(sData); + _currentSelectedChainId = _currentSelectedChainId ?? sData.chainId; await _setLocalEthChain(_currentSelectedChainId!, logEvent: false); } catch (e, s) { _logger.e( @@ -489,10 +501,12 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { _currentSelectedChainId = chainId; _notify(); try { - await _storage.set( - StorageConstants.selectedChainId, - {'chainId': _currentSelectedChainId!}, - ); + if (isConnected) { + await _storage.set( + StorageConstants.selectedChainId, + {'chainId': _currentSelectedChainId!}, + ); + } } catch (e) { _logger.e('[$runtimeType] _setLocalEthChain error: $e'); } @@ -1594,12 +1608,18 @@ extension _EmailConnectorExtension on ReownAppKitModal { // Login event should be treated like Connect event for regular wallets Future _onMagicLoginEvent(MagicLoginEvent? args) async { final debugString = jsonEncode(args?.data?.toJson()); - _logger.i('[$runtimeType] _onMagicLoginEvent: $debugString'); + _logger.d('[$runtimeType] _onMagicLoginEvent: $debugString'); if (args!.data != null) { final newChainId = _getStoredChainId('${args.data!.chainId}')!; _currentSelectedChainId = newChainId; // - final magicData = args.data?.copytWith(chainId: int.tryParse(newChainId)); + final magicData = args.data?.copytWith( + chainId: int.tryParse(newChainId), + email: args.data?.email ?? _currentSession?.toRawJson()['email'], + userName: + args.data?.userName ?? _currentSession?.toRawJson()['userName'], + ); + final session = ReownAppKitModalSession(magicData: magicData); await _setSesionAndChainData(session); onModalConnect.broadcast(ModalConnect(session)); @@ -1637,22 +1657,24 @@ extension _EmailConnectorExtension on ReownAppKitModal { final chainId = args.chainId?.toString() ?? _currentSession!.chainId; _currentSelectedChainId = chainId; // - final session = _currentSession!.copyWith( - magicData: MagicData( - email: newEmail, - address: address, - chainId: int.parse(chainId), - peer: magicService.instance.metadata, - self: ConnectionMetadata( - metadata: _appKit.metadata, - publicKey: '', - ), - ), + final magicData = MagicData( + email: newEmail, + address: address, + chainId: int.parse(chainId), + // peer: _currentSession?.peer, + // self: _currentSession?.self, ); + final session = (_currentSession != null) + ? _currentSession!.copyWith( + magicData: magicData, + ) + : ReownAppKitModalSession( + magicData: magicData, + ); await _setSesionAndChainData(session); onModalUpdate.broadcast(ModalConnect(session)); } catch (e, s) { - _logger.d( + _logger.e( '[$runtimeType] _onMagicUpdateEvent: $e', stackTrace: s, ); @@ -1681,6 +1703,7 @@ extension _EmailConnectorExtension on ReownAppKitModal { } Future _onMagicConnectEvent(MagicConnectEvent? event) async { + _logger.d('[$runtimeType] _onMagicConnectEvent: $event'); if (event?.connected == false) { if (_currentSession != null) { onModalConnect.broadcast(ModalConnect(_currentSession!)); @@ -1692,7 +1715,7 @@ extension _EmailConnectorExtension on ReownAppKitModal { extension _CoinbaseConnectorExtension on ReownAppKitModal { void _onCoinbaseConnectEvent(CoinbaseConnectEvent? args) async { final debugString = jsonEncode(args?.data?.toJson()); - _logger.i('[$runtimeType] _onCoinbaseConnectEvent: $debugString'); + _logger.d('[$runtimeType] _onCoinbaseConnectEvent: $debugString'); if (args?.data != null) { final newChainId = _getStoredChainId('${args!.data!.chainId}')!; _currentSelectedChainId = newChainId; @@ -1715,7 +1738,7 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { } void _onCoinbaseSessionUpdateEvent(CoinbaseSessionEvent? args) async { - _logger.i('[$runtimeType] _onCoinbaseSessionUpdateEvent $args'); + _logger.d('[$runtimeType] _onCoinbaseSessionUpdateEvent $args'); if (args != null) { try { final address = args.address ?? _currentSession!.address!; @@ -1741,7 +1764,7 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { await _setSesionAndChainData(session); onModalUpdate.broadcast(ModalConnect(session)); } catch (e, s) { - _logger.d( + _logger.e( '[$runtimeType] _onCoinbaseSessionUpdateEvent: $e', stackTrace: s, ); @@ -1761,7 +1784,7 @@ extension _CoinbaseConnectorExtension on ReownAppKitModal { extension _AppKitModalExtension on ReownAppKitModal { void _onSessionAuthResponse(SessionAuthResponse? args) async { final debugString = jsonEncode(args?.toJson()); - dev.log('[$runtimeType] _onSessionAuthResponse: $debugString'); + _logger.d('[$runtimeType] _onSessionAuthResponse: $debugString'); if (args?.session != null) { // IF 1-CA SUPPORTED WE SHOULD CALL SIWECONGIF METHODS HERE final session = await _settleSession(args!.session!); @@ -1799,7 +1822,7 @@ extension _AppKitModalExtension on ReownAppKitModal { void _onSessionConnect(SessionConnect? args) async { final debugString = jsonEncode(args?.session.toJson()); - dev.log('[$runtimeType] _onSessionConnect: $debugString'); + _logger.d('[$runtimeType] _onSessionConnect: $debugString'); final siweEnabled = siweService.instance!.enabled; if (_supportsOneClickAuth && siweEnabled) return; if (args != null) { @@ -1822,7 +1845,8 @@ extension _AppKitModalExtension on ReownAppKitModal { // HAS TO BE CALLED JUST ONCE ON CONNECTION Future _settleSession( - SessionData sessionData) async { + SessionData sessionData, + ) async { if (_currentSelectedChainId == null) { final chains = NamespaceUtils.getChainIdsFromNamespaces( namespaces: sessionData.namespaces, @@ -1851,6 +1875,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _oneSIWEFinish(ReownAppKitModalSession updatedSession) async { + _logger.d('[$runtimeType] _oneSIWEFinish $updatedSession'); await _storeSession(updatedSession); try { await _storage.set( @@ -1869,7 +1894,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _onSessionEvent(SessionEvent? args) async { - _logger.i('[$runtimeType] session event $args'); + _logger.d('[$runtimeType] session event $args'); onSessionEventEvent.broadcast(args); if (args?.name == EventsConstants.chainChanged) { _currentSelectedChainId = args?.data?.toString(); @@ -1885,7 +1910,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _onSessionUpdate(SessionUpdate? args) async { - _logger.i('[$runtimeType] session update $args'); + _logger.d('[$runtimeType] session update $args'); if (args != null) { final wcSessions = _appKit.sessions.getAll(); if (wcSessions.isEmpty) return; @@ -1903,7 +1928,7 @@ extension _AppKitModalExtension on ReownAppKitModal { } void _onSessionExpire(SessionExpire? args) { - _logger.i('[$runtimeType] session expire $args'); + _logger.d('[$runtimeType] session expire $args'); onSessionExpireEvent.broadcast(args); } diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart index cd94285..0dde4c1 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_modal_session.dart @@ -170,21 +170,25 @@ class ReownAppKitModalSession { // toJson() would convert ReownAppKitModalSession to a SessionData kind of map // no matter if Coinbase Wallet or Email Wallet is connected - Map toJson() => { - if (topic != null) 'topic': topic, - if (pairingTopic != null) 'pairingTopic': pairingTopic, - if (relay != null) 'relay': relay, - if (expiry != null) 'expiry': expiry, - if (acknowledged != null) 'acknowledged': acknowledged, - if (controller != null) 'controller': controller, - 'namespaces': _namespaces(), - if (requiredNamespaces != null) - 'requiredNamespaces': requiredNamespaces, - if (optionalNamespaces != null) - 'optionalNamespaces': optionalNamespaces, - 'self': self?.toJson(), - 'peer': peer?.toJson(), - }; + Map toJson() { + final sessionData = SessionData( + topic: topic ?? '', + pairingTopic: pairingTopic ?? '', + relay: relay ?? Relay(ReownConstants.RELAYER_DEFAULT_PROTOCOL), + expiry: expiry ?? 0, + acknowledged: acknowledged ?? false, + controller: controller ?? '', + namespaces: _namespaces() ?? {}, + self: self!, + peer: peer!, + requiredNamespaces: _sessionData?.requiredNamespaces, + optionalNamespaces: _sessionData?.optionalNamespaces, + sessionProperties: _sessionData?.sessionProperties, + authentication: _sessionData?.authentication, + transportType: _sessionData?.transportType ?? TransportType.relay, + ); + return sessionData.toJson(); + } } extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { @@ -195,18 +199,22 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { bool? get acknowledged => _sessionData?.acknowledged; String? get controller => _sessionData?.controller; Map? get namespaces => _sessionData?.namespaces; - Map? get requiredNamespaces => - _sessionData?.requiredNamespaces; - Map? get optionalNamespaces => - _sessionData?.optionalNamespaces; - Map? get sessionProperties => _sessionData?.sessionProperties; ConnectionMetadata? get self { if (sessionService.isCoinbase) { return _coinbaseData?.self; } if (sessionService.isMagic) { - return _magicData?.self; + return _magicData?.self ?? + ConnectionMetadata( + publicKey: '', + metadata: PairingMetadata( + name: 'Email Wallet', + description: '', + url: '', + icons: [], + ), + ); } return _sessionData?.self; } @@ -216,7 +224,16 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { return _coinbaseData?.peer; } if (sessionService.isMagic) { - return _magicData?.peer; + return _magicData?.peer ?? + ConnectionMetadata( + publicKey: '', + metadata: PairingMetadata( + name: 'Email Wallet', + description: '', + url: '', + icons: [], + ), + ); } return _sessionData?.peer; } @@ -265,9 +282,6 @@ extension ReownAppKitModalSessionExtension on ReownAppKitModalSession { if (sessionService.isCoinbase) { return CoinbaseService.defaultWalletData.listing.name; } - if (sessionService.isMagic) { - return MagicService.defaultWalletData.listing.name; - } if (sessionService.isWC) { return peer?.metadata.name; } diff --git a/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart b/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart index 2838128..78fc7af 100644 --- a/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart +++ b/packages/reown_appkit/lib/modal/models/public/appkit_social_options.dart @@ -1,11 +1,11 @@ enum AppKitSocialOption { - Google, + // Google, X, Github, Discord, Apple, Facebook, Farcaster, - Twitch, - Telegram, + // Twitch, + // Telegram, } diff --git a/packages/reown_appkit/lib/modal/pages/all_social_logins.dart b/packages/reown_appkit/lib/modal/pages/all_social_logins.dart new file mode 100644 index 0000000..e5abf55 --- /dev/null +++ b/packages/reown_appkit/lib/modal/pages/all_social_logins.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; +import 'package:reown_appkit/modal/constants/key_constants.dart'; +import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/utils/asset_util.dart'; +import 'package:reown_appkit/modal/widgets/lists/list_items/base_list_item.dart'; +import 'package:reown_appkit/modal/widgets/navigation/navbar.dart'; +import 'package:reown_appkit/reown_appkit.dart'; + +class AllSocialLoginsPage extends StatefulWidget { + const AllSocialLoginsPage({ + required this.onSelect, + }) : super(key: KeyConstants.allSocialLoginPageKey); + final Function(AppKitSocialOption) onSelect; + + @override + State createState() => + _AppKitModalMainWalletsPageState(); +} + +class _AppKitModalMainWalletsPageState extends State { + @override + Widget build(BuildContext context) { + final themeData = ReownAppKitModalTheme.getDataOf(context); + final themeColors = ReownAppKitModalTheme.colorsOf(context); + final listItems = magicService.instance.socials + .map( + (item) => BaseListItem( + onTap: () => widget.onSelect(item), + child: Row( + children: [ + LayoutBuilder( + builder: (_, constraints) { + return SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${item.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + height: constraints.maxHeight, + width: constraints.maxHeight, + ); + }, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Text( + item.name, + style: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + ), + ), + ], + ), + ), + ) + .toList(); + return ModalNavbar( + title: 'All socials', + safeAreaLeft: true, + safeAreaRight: true, + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: kPadding12), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox.square(dimension: kListViewSeparatorHeight), + const SizedBox.square(dimension: kListViewSeparatorHeight), + ..._buttonsWithDivider(listItems), + ], + ), + ), + ), + ); + } + + List _buttonsWithDivider(List widgets) { + List spacedWidgets = []; + for (int i = 0; i < widgets.length; i++) { + spacedWidgets.add(widgets[i]); + if (i < widgets.length - 1) { + spacedWidgets.add( + const SizedBox.square(dimension: kListViewSeparatorHeight), + ); + } + } + return spacedWidgets; + } +} diff --git a/packages/reown_appkit/lib/modal/pages/approve_magic_request_page.dart b/packages/reown_appkit/lib/modal/pages/approve_magic_request_page.dart index 52d01d9..3dfa6aa 100644 --- a/packages/reown_appkit/lib/modal/pages/approve_magic_request_page.dart +++ b/packages/reown_appkit/lib/modal/pages/approve_magic_request_page.dart @@ -20,7 +20,7 @@ class _ApproveTransactionPageState extends State { ); return ModalNavbar( title: 'Approve Transaction', - noClose: true, + // noClose: true, safeAreaLeft: true, safeAreaRight: true, body: ConstrainedBox( diff --git a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart index ae8e92d..f40af0a 100644 --- a/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart +++ b/packages/reown_appkit/lib/modal/pages/public/appkit_modal_main_wallets_page.dart @@ -36,16 +36,18 @@ class _AppKitModalMainWalletsPageState @override void initState() { super.initState(); - magicService.instance.isEnabled.addListener(_mailEnabledListener); + magicService.instance.isEmailEnabled.addListener(_enabledListener); + magicService.instance.isSocialEnabled.addListener(_enabledListener); } - void _mailEnabledListener() { + void _enabledListener() { setState(() {}); } @override void dispose() { - magicService.instance.isEnabled.removeListener(_mailEnabledListener); + magicService.instance.isSocialEnabled.removeListener(_enabledListener); + magicService.instance.isEmailEnabled.removeListener(_enabledListener); super.dispose(); } @@ -84,10 +86,14 @@ class _AppKitModalMainWalletsPageState if (itemsCount < kShortWalletListCount) { maxHeight = kListItemHeight * (itemsCount + 1.5); } - final emailEnabled = magicService.instance.isEnabled.value; + final emailEnabled = magicService.instance.isEmailEnabled.value; if (emailEnabled) { - // TODO multiply depending on social options - maxHeight += (kEmailFieldHeight * 4); + maxHeight += (kListItemHeight * 1); + } + final socialEnabled = magicService.instance.isSocialEnabled.value; + if (socialEnabled) { + final count = magicService.instance.socials.length > 1 ? 3 : 2; + maxHeight += (kListItemHeight * count); } final itemsToShow = items.getRange(0, itemsCount); return ConstrainedBox( @@ -100,39 +106,55 @@ class _AppKitModalMainWalletsPageState firstItem: Column( children: [ EmailLoginInputField(), - SocialLoginButtonsView(), - _LoginDivider(), - const SizedBox.square(dimension: kListViewSeparatorHeight), + Visibility( + visible: emailEnabled || socialEnabled, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + ), + child: Column( + children: [ + SocialLoginButtonsView(), + _LoginDivider(), + ], + ), + ), + ), ], ), itemList: itemsToShow.toList(), bottomItems: [ - AllWalletsItem( - trailing: (items.length <= kShortWalletListCount) - ? null - : ValueListenableBuilder( - valueListenable: - explorerService.instance.totalListings, - builder: (context, value, _) { - return WalletItemChip(value: value.lazyCount); - }, - ), - onTap: () { - if (items.length <= kShortWalletListCount) { - widgetStack.instance.push( - const ReownAppKitModalQRCodePage(), - event: SelectWalletEvent( - name: 'WalletConnect', - platform: AnalyticsPlatform.qrcode, - ), - ); - } else { - widgetStack.instance.push( - const ReownAppKitModalAllWalletsPage(), - event: ClickAllWalletsEvent(), - ); - } - }, + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + ), + child: AllWalletsItem( + trailing: (items.length <= kShortWalletListCount) + ? null + : ValueListenableBuilder( + valueListenable: + explorerService.instance.totalListings, + builder: (context, value, _) { + return WalletItemChip(value: value.lazyCount); + }, + ), + onTap: () { + if (items.length <= kShortWalletListCount) { + widgetStack.instance.push( + const ReownAppKitModalQRCodePage(), + event: SelectWalletEvent( + name: 'WalletConnect', + platform: AnalyticsPlatform.qrcode, + ), + ); + } else { + widgetStack.instance.push( + const ReownAppKitModalAllWalletsPage(), + event: ClickAllWalletsEvent(), + ); + } + }, + ), ), ], ), diff --git a/packages/reown_appkit/lib/modal/pages/social_login_page.dart b/packages/reown_appkit/lib/modal/pages/social_login_page.dart index 59f6b60..5124067 100644 --- a/packages/reown_appkit/lib/modal/pages/social_login_page.dart +++ b/packages/reown_appkit/lib/modal/pages/social_login_page.dart @@ -1,3 +1,7 @@ +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:reown_appkit/modal/constants/key_constants.dart'; @@ -6,14 +10,23 @@ import 'package:reown_appkit/modal/constants/string_constants.dart'; import 'package:reown_appkit/modal/i_appkit_modal_impl.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; +import 'package:reown_appkit/modal/services/toast_service/models/toast_message.dart'; +import 'package:reown_appkit/modal/services/toast_service/toast_service_singleton.dart'; import 'package:reown_appkit/modal/utils/asset_util.dart'; +import 'package:reown_appkit/modal/utils/platform_utils.dart'; +import 'package:reown_appkit/modal/widgets/buttons/simple_icon_button.dart'; +import 'package:reown_appkit/modal/widgets/icons/rounded_icon.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/content_loading.dart'; import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.dart'; import 'package:reown_appkit/modal/widgets/modal_provider.dart'; import 'package:reown_appkit/modal/widgets/avatars/loading_border.dart'; import 'package:reown_appkit/modal/widgets/navigation/navbar.dart'; +import 'package:reown_appkit/modal/widgets/navigation/navbar_action_button.dart'; +import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; import 'package:reown_appkit/reown_appkit.dart'; import 'package:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_android/webview_flutter_android.dart'; +import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; class SocialLoginPage extends StatefulWidget { const SocialLoginPage({required this.socialOption}) @@ -24,15 +37,14 @@ class SocialLoginPage extends StatefulWidget { State createState() => _SocialLoginPageState(); } -class _SocialLoginPageState extends State - with WidgetsBindingObserver { +class _SocialLoginPageState extends State { IReownAppKitModal? _service; ModalError? errorEvent; @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + // WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { _service = ModalProvider.of(context).instance; _service?.onModalError.subscribe(_errorListener); @@ -42,48 +54,90 @@ class _SocialLoginPageState extends State } @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed) { - // final isOpen = _service?.isOpen ?? false; - // final isConnected = _service?.isConnected ?? false; - // if (isOpen && isConnected && !siweService.instance!.enabled) { - // Future.delayed(Duration(seconds: 1), () { - // if (!mounted) return; - // _service?.closeModal(); - // }); - // } - } + void dispose() { + _service?.onModalError.unsubscribe(_errorListener); + // WidgetsBinding.instance.removeObserver(this); + super.dispose(); } - void _errorListener(ModalError? event) => setState(() => errorEvent = event); + void _errorListener(ModalError? event) { + toastService.instance.show(ToastMessage( + type: ToastType.error, + text: event?.message ?? 'Something went wrong.', + )); + setState(() => errorEvent = event); + } void _initSocialLogin(AppKitSocialOption option) async { try { + setState(() => errorEvent = null); final appKitModal = ModalProvider.of(context).instance; final redirectUri = await magicService.instance.getSocialRedirectUri( - provider: option.name.toLowerCase(), + provider: option, chainId: appKitModal.selectedChain?.chainId, ); - debugPrint('[$runtimeType] _initSocialLogin uri $redirectUri'); - final result = await Navigator.push( - context, - MaterialPageRoute( - fullscreenDialog: true, - barrierDismissible: true, - builder: (context) => _WebViewLoginWidget(url: redirectUri!), - ), - ); - // await ReownCoreUtils.openURL(uri!); - // await _launchUrlInBottomSheet(uri!); - await _completeSocialLogin(result); + setState(() => errorEvent = null); + if (option.supportsWebView) { + await _continueInWebview(redirectUri!); + } else { + await ReownCoreUtils.openURL(redirectUri!); + // await _launchUrlInBottomSheet(uri!); + // magicService.instance.onSocialLoginEvent.subscribe((event) async { + // await _completeSocialLogin(event!.uri!); + // }); + } } catch (e) { debugPrint('[$runtimeType] _initSocialLogin error $e'); } } + bool _retrievingData = false; + Future _continueInWebview(String redirectUri) async { + final themeColors = ReownAppKitModalTheme.colorsOf(context); + final radiuses = ReownAppKitModalTheme.radiusesOf(context); + final bottomSheet = PlatformUtils.isBottomSheet(); + final isTabletSize = PlatformUtils.isTablet(context); + final maxRadius = min(radiuses.radiusM, 36.0); + final innerContainerBorderRadius = bottomSheet && !isTabletSize + ? BorderRadius.only( + topLeft: Radius.circular(maxRadius), + topRight: Radius.circular(maxRadius), + ) + : BorderRadius.all(Radius.circular(maxRadius)); + final result = await showModalBottomSheet( + backgroundColor: Colors.black.withOpacity(0.5), + isDismissible: false, + isScrollControlled: true, + enableDrag: false, + useRootNavigator: false, + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.9, + ), + context: context, + builder: (context) => Container( + decoration: BoxDecoration( + color: themeColors.background125, + borderRadius: innerContainerBorderRadius, + ), + child: _WebViewLoginWidget( + url: redirectUri, + onCancel: () { + Navigator.of(context).pop(false); + }, + ), + ), + ); + + if (result == false) { + _cancelSocialLogin(); + } else { + await _completeSocialLogin(result); + } + } + Future _completeSocialLogin(String url) async { try { - debugPrint('[$runtimeType] _completeSocialLogin $url'); + setState(() => _retrievingData = true); final uri = Uri.parse(url); final result = await magicService.instance.connectSocial( uri: '?${uri.query}', @@ -94,14 +148,13 @@ class _SocialLoginPageState extends State } } catch (e) { debugPrint('[$runtimeType] _completeSocialLogin error $e'); + setState(() => _retrievingData = false); } } - @override - void dispose() { - _service?.onModalError.unsubscribe(_errorListener); - WidgetsBinding.instance.removeObserver(this); - super.dispose(); + void _cancelSocialLogin() { + errorEvent = ModalError('User canceled'); + setState(() => _retrievingData = false); } @override @@ -120,9 +173,7 @@ class _SocialLoginPageState extends State return ModalNavbar( title: widget.socialOption.name, onBack: () { - // TODO Social Login cancel Social Login flow - // _service?.selectWallet(null); - // widgetStack.instance.pop(); + widgetStack.instance.pop(); }, body: SingleChildScrollView( scrollDirection: isPortrait ? Axis.vertical : Axis.horizontal, @@ -136,19 +187,54 @@ class _SocialLoginPageState extends State mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox.square(dimension: 20.0), - LoadingBorder( - animate: errorEvent == null, - borderRadius: themeData.radiuses.isSquare() - ? 0 - : themeData.radiuses.radiusM + 4.0, - child: SvgPicture.asset( - AssetUtils.getThemedAsset( - context, - '${widget.socialOption.name.toLowerCase()}_logo.svg', - ), - package: 'reown_appkit', - ), - ), + errorEvent == null + ? LoadingBorder( + animate: errorEvent == null, + borderRadius: maxWidth / 2, + child: SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${widget.socialOption.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + ), + ) + : Stack( + children: [ + SizedBox.square( + dimension: kSelectedWalletIconHeight, + child: SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${widget.socialOption.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + ), + ), + Positioned( + bottom: 0, + right: 0, + child: Container( + decoration: BoxDecoration( + color: themeColors.background125, + borderRadius: + BorderRadius.all(Radius.circular(30.0)), + ), + padding: const EdgeInsets.all(1.0), + clipBehavior: Clip.antiAlias, + child: RoundedIcon( + assetPath: 'lib/modal/assets/icons/close.svg', + assetColor: themeColors.error100, + circleColor: + themeColors.error100.withOpacity(0.2), + borderColor: themeColors.background125, + padding: 4.0, + size: 24.0, + ), + ), + ), + ], + ), const SizedBox.square(dimension: 20.0), Text( 'Log in with ${widget.socialOption.name}', @@ -158,13 +244,25 @@ class _SocialLoginPageState extends State ), ), const SizedBox.square(dimension: 8.0), - Text( - 'Connect in the provider window', - textAlign: TextAlign.center, - style: themeData.textStyles.small500.copyWith( - color: themeColors.foreground200, - ), - ), + errorEvent == null + ? Text( + _retrievingData + ? 'Retrieving user data' + : 'Connect in the provider window', + textAlign: TextAlign.center, + style: themeData.textStyles.small500.copyWith( + color: themeColors.foreground200, + ), + ) + : SimpleIconButton( + onTap: () { + _initSocialLogin(widget.socialOption); + }, + leftIcon: 'lib/modal/assets/icons/refresh_back.svg', + title: 'Try again', + backgroundColor: Colors.transparent, + foregroundColor: themeColors.accent100, + ), const SizedBox.square(dimension: kPadding16), ], ), @@ -178,8 +276,12 @@ class _SocialLoginPageState extends State } class _WebViewLoginWidget extends StatefulWidget { - const _WebViewLoginWidget({required this.url}); + const _WebViewLoginWidget({ + required this.url, + required this.onCancel, + }); final String url; + final VoidCallback onCancel; @override State<_WebViewLoginWidget> createState() => __WebViewLoginWidgetState(); @@ -187,6 +289,7 @@ class _WebViewLoginWidget extends StatefulWidget { class __WebViewLoginWidgetState extends State<_WebViewLoginWidget> { final _webViewController = WebViewController(); + final _cookieManager = WebViewCookieManager(); final _authority = Uri.parse(UrlConstants.secureDashboard).authority; @override @@ -195,7 +298,55 @@ class __WebViewLoginWidgetState extends State<_WebViewLoginWidget> { _init(); } + Future _clearCookies() async { + if (kDebugMode) return; + try { + if (WebViewPlatform.instance is WebKitWebViewPlatform) { + final webKitManager = + _cookieManager.platform as WebKitWebViewCookieManager; + webKitManager.clearCookies(); + } else if (WebViewPlatform.instance is AndroidWebViewPlatform) { + final androidManager = + _cookieManager.platform as AndroidWebViewCookieManager; + androidManager.clearCookies(); + androidManager.setAcceptThirdPartyCookies( + _webViewController.platform as AndroidWebViewController, + kDebugMode, + ); + } + } catch (e) { + debugPrint('[$runtimeType] _clearCookies error $e'); + } + } + + void _setDebugMode() { + if (kDebugMode) { + if (Platform.isIOS) { + final wkCtrl = _webViewController.platform as WebKitWebViewController; + wkCtrl.setInspectable(true); + } + if (Platform.isAndroid) { + if (_webViewController.platform is AndroidWebViewController) { + final aCtrl = _webViewController.platform as AndroidWebViewController; + aCtrl.setMediaPlaybackRequiresUserGesture(false); + AndroidWebViewController.enableDebugging(true); + + final cookieManager = + _cookieManager.platform as AndroidWebViewCookieManager; + cookieManager.setAcceptThirdPartyCookies( + _webViewController.platform as AndroidWebViewController, + true, + ); + } + } + } + } + Future _init() async { + _setDebugMode(); + await _clearCookies(); + await _webViewController.clearCache(); + await _webViewController.clearLocalStorage(); await _webViewController.setNavigationDelegate( NavigationDelegate( onNavigationRequest: (NavigationRequest request) { @@ -216,11 +367,40 @@ class __WebViewLoginWidgetState extends State<_WebViewLoginWidget> { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: WebViewWidget( - controller: _webViewController, + return ModalNavbar( + title: '', + onBack: () => widget.onCancel(), + noClose: true, + rightAction: NavbarActionButton( + asset: 'lib/modal/assets/icons/close.svg', + action: () => widget.onCancel(), ), + body: WebViewWidget(controller: _webViewController), ); } } + +extension _AppKitSocialOptionExtension on AppKitSocialOption { + bool get supportsWebView { + switch (this) { + case AppKitSocialOption.X: + return true; + case AppKitSocialOption.Apple: + return true; + case AppKitSocialOption.Discord: + return true; + case AppKitSocialOption.Github: + return true; + case AppKitSocialOption.Facebook: + return true; + case AppKitSocialOption.Farcaster: + return true; + // case AppKitSocialOption.Twitch: + // return true; + // case AppKitSocialOption.Telegram: + // return true; + // case AppKitSocialOption.Google: + // return false; + } + } +} diff --git a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart index 36d2f17..b2ea642 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/i_magic_service.dart @@ -2,16 +2,16 @@ import 'package:reown_appkit/modal/services/magic_service/models/magic_events.da import 'package:reown_appkit/reown_appkit.dart'; abstract class IMagicService { - ConnectionMetadata get metadata; List get socials; Future init(); void setEmail(String value); void setNewEmail(String value); + void setProvider(String? value); // ****** W3mFrameProvider public methods ******* // - Future getSocialRedirectUri({required String provider}); + Future getSocialRedirectUri({required AppKitSocialOption provider}); Future connectSocial({required String uri}); Future connectEmail({required String value}); @@ -35,4 +35,5 @@ abstract class IMagicService { abstract final Event onMagicUpdate; abstract final Event onMagicError; abstract final Event onMagicRpcRequest; + // abstract final Event onSocialLoginEvent; } diff --git a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart index 9b5f328..f767581 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/magic_service.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:reown_appkit/reown_appkit.dart'; @@ -24,32 +25,19 @@ class MagicService implements IMagicService { 'auth.magic.link', 'launchdarkly.com', ]; - static const supportedMethods = [ - 'personal_sign', - 'eth_sign', - 'eth_sendTransaction', - 'eth_signTypedData_v4', - 'wallet_switchEthereumChain', - 'wallet_addEthereumChain', - ]; - static const defaultWalletData = ReownAppKitModalWalletInfo( - listing: Listing( - id: '', - name: 'Email Wallet', - homepage: '', - imageId: '', - order: 10000, - ), - installed: false, - recent: false, - ); + static const supportedMethods = MethodsConstants.allMethods; - @override - ConnectionMetadata get metadata => ConnectionMetadata( + ConnectionMetadata get _selfMetadata => ConnectionMetadata( + metadata: _metadata, + publicKey: '', + ); + + // TODO export this + ConnectionMetadata get _peerMetadata => ConnectionMetadata( metadata: PairingMetadata( - name: defaultWalletData.listing.name, + name: 'Email Wallet', description: '', - url: defaultWalletData.listing.homepage, + url: '', icons: [''], // TODO set the icon here depending on the login type ), publicKey: '', @@ -60,6 +48,7 @@ class MagicService implements IMagicService { String? _connectionChainId; int _onLoadCount = 0; String _packageName = ''; + AppKitSocialOption? _socialProvider; late final WebViewController _webViewController; WebViewController get controller => _webViewController; @@ -90,7 +79,11 @@ class MagicService implements IMagicService { @override Event onMagicRpcRequest = Event(); - final isEnabled = ValueNotifier(false); + // @override + // Event onSocialLoginEvent = Event(); + + final isEmailEnabled = ValueNotifier(false); + final isSocialEnabled = ValueNotifier(false); final isReady = ValueNotifier(false); final isConnected = ValueNotifier(false); final isTimeout = ValueNotifier(false); @@ -109,13 +102,15 @@ class MagicService implements IMagicService { MagicService({ required IReownCore core, required PairingMetadata metadata, - required List socials, - bool enabled = false, + List socials = const [], + bool enableEmail = false, }) : _core = core, _metadata = metadata, _socialOptions = socials { - isEnabled.value = enabled; - if (isEnabled.value) { + isEmailEnabled.value = enableEmail; + isSocialEnabled.value = _socialOptions.isNotEmpty; + // + if (isEmailEnabled.value || isSocialEnabled.value) { _webViewController = WebViewController(); _webview = WebViewWidget(controller: _webViewController); isReady.addListener(_readyListener); @@ -131,7 +126,7 @@ class MagicService implements IMagicService { @override Future init() async { - if (!isEnabled.value) { + if (!isEmailEnabled.value && !isSocialEnabled.value) { _initialized = Completer(); _initialized.complete(false); _connected = Completer(); @@ -201,19 +196,35 @@ class MagicService implements IMagicService { newEmail.value = value; } + @override + void setProvider(String? value) { + _socialProvider = AppKitSocialOption.values.firstWhereOrNull( + (e) => e.name.toLowerCase() == value, + ); + } + + bool get _socialsNotReady => (!isSocialEnabled.value || !isReady.value); + bool get _emailNotReady => (!isEmailEnabled.value || !isReady.value); + bool get _serviceNotReady => + (!isEmailEnabled.value && !isSocialEnabled.value) || !isReady.value; + // ****** W3mFrameProvider public methods ******* // + // SOCIAL RELATED METHODS + Completer _getSocialRedirectUri = Completer(); @override Future getSocialRedirectUri({ - required String provider, + required AppKitSocialOption provider, String? chainId, }) async { - if (!isEnabled.value || !isReady.value) return null; + if (_socialsNotReady) return null; + // _getSocialRedirectUri = Completer(); _connectionChainId = chainId ?? _connectionChainId; + _socialProvider = provider; final message = GetSocialRedirectUri( - provider: provider.toLowerCase(), + provider: _socialProvider!.name.toLowerCase(), ).toString(); await _webViewController.runJavaScript('sendMessage($message)'); return await _getSocialRedirectUri.future; @@ -222,16 +233,21 @@ class MagicService implements IMagicService { Completer _connectSocial = Completer(); @override Future connectSocial({required String uri}) async { - if (!isEnabled.value || !isReady.value) return null; + if (_socialsNotReady) return null; + // _connectSocial = Completer(); final message = ConnectSocial(uri: uri).toString(); await _webViewController.runJavaScript('sendMessage($message)'); return await _connectSocial.future; } + // EMAIL RELATED METHODS + @override Future connectEmail({required String value, String? chainId}) async { - if (!isEnabled.value || !isReady.value) return; + if (_emailNotReady) return; + // + _socialProvider = null; _connectionChainId = chainId ?? _connectionChainId; final message = ConnectEmail(email: value).toString(); await _webViewController.runJavaScript('sendMessage($message)'); @@ -239,7 +255,8 @@ class MagicService implements IMagicService { @override Future updateEmail({required String value}) async { - if (!isEnabled.value || !isReady.value) return; + if (_emailNotReady) return; + // step.value = EmailLoginStep.loading; final message = UpdateEmail(email: value).toString(); await _webViewController.runJavaScript('sendMessage($message)'); @@ -247,7 +264,8 @@ class MagicService implements IMagicService { @override Future updateEmailPrimaryOtp({required String otp}) async { - if (!isEnabled.value || !isReady.value) return; + if (_emailNotReady) return; + // step.value = EmailLoginStep.loading; final message = UpdateEmailPrimaryOtp(otp: otp).toString(); await _webViewController.runJavaScript('sendMessage($message)'); @@ -255,7 +273,8 @@ class MagicService implements IMagicService { @override Future updateEmailSecondaryOtp({required String otp}) async { - if (!isEnabled.value || !isReady.value) return; + if (_emailNotReady) return; + // step.value = EmailLoginStep.loading; final message = UpdateEmailSecondaryOtp(otp: otp).toString(); await _webViewController.runJavaScript('sendMessage($message)'); @@ -263,28 +282,26 @@ class MagicService implements IMagicService { @override Future connectOtp({required String otp}) async { - if (!isEnabled.value || !isReady.value) return; + if (_emailNotReady) return; + // step.value = EmailLoginStep.loading; final message = ConnectOtp(otp: otp).toString(); await _webViewController.runJavaScript('sendMessage($message)'); } - @override - Future getChainId() async { - if (!isEnabled.value || !isReady.value) return; - final message = GetChainId().toString(); - await _webViewController.runJavaScript('sendMessage($message)'); - } + // SHARED METHODS @override Future syncTheme(ReownAppKitModalTheme? theme) async { - if (!isEnabled.value || !isReady.value) return; + if (_serviceNotReady) return; + // final message = SyncTheme(theme: theme).toString(); await _webViewController.runJavaScript('sendMessage($message)'); } void _syncDappData() async { - if (!isEnabled.value || !isReady.value) return; + if (_serviceNotReady) return; + // final message = SyncAppData( metadata: _metadata, projectId: _core.projectId, @@ -293,9 +310,18 @@ class MagicService implements IMagicService { await _webViewController.runJavaScript('sendMessage($message)'); } + @override + Future getChainId() async { + if (_serviceNotReady) return; + // + final message = GetChainId().toString(); + await _webViewController.runJavaScript('sendMessage($message)'); + } + @override Future getUser({String? chainId}) async { - if (!isEnabled.value || !isReady.value) return; + if (_serviceNotReady) return; + // return await _getUser(chainId); } @@ -306,7 +332,8 @@ class MagicService implements IMagicService { @override Future switchNetwork({required String chainId}) async { - if (!isEnabled.value || !isReady.value) return; + if (_serviceNotReady) return; + // final message = SwitchNetwork(chainId: chainId).toString(); await _webViewController.runJavaScript('sendMessage($message)'); } @@ -316,7 +343,8 @@ class MagicService implements IMagicService { String? chainId, required SessionRequestParams request, }) async { - if (!isEnabled.value) return; + if (_serviceNotReady) return; + // await _awaitReadyness.future; await _rpcRequest(request.toJson()); return await _response.future; @@ -340,7 +368,8 @@ class MagicService implements IMagicService { @override Future disconnect() async { - if (!isEnabled.value || !isReady.value) return false; + if (_serviceNotReady) return false; + // _disconnect = Completer(); if (!isConnected.value) { _resetTimeOut(); @@ -412,17 +441,16 @@ class MagicService implements IMagicService { final uri = messageData.getPayloadMapKey('uri'); _getSocialRedirectUri.complete(uri); } - if (messageData.getSocialRedirectUriError) { - // TODO Social Login capture and pass error - _getSocialRedirectUri.complete(null); - } if (messageData.connectSocialSuccess) { + final data = MagicData.fromJson(messageData.payload!); + final event = MagicSessionEvent( + email: data.email ?? data.userName, + address: data.address, + chainId: data.chainId, + ); + onMagicUpdate.broadcast(event); _connectSocial.complete(true); } - if (messageData.connectSocialError) { - // TODO Social Login capture and pass error - _connectSocial.complete(false); - } // ****** CONNECT_EMAIL if (messageData.connectEmailSuccess) { if (step.value != EmailLoginStep.loading) { @@ -503,11 +531,13 @@ class MagicService implements IMagicService { _connected.complete(isConnected.value); } else { final session = data.copytWith( - peer: metadata, - self: ConnectionMetadata( - metadata: _metadata, - publicKey: '', + peer: _peerMetadata.copyWith( + metadata: _peerMetadata.metadata.copyWith( + name: _socialProvider?.name ?? 'Email Wallet', + ), ), + self: _selfMetadata, + provider: _socialProvider, ); onMagicLoginSuccess.broadcast(MagicLoginEvent(session)); } @@ -548,6 +578,24 @@ class MagicService implements IMagicService { final message = messageData.payload?['message']?.toString(); _error(ConnectOtpErrorEvent(message: message)); } + if (messageData.getSocialRedirectUriError) { + String? message = messageData.payload?['message']?.toString(); + message = message?.replaceFirst( + 'Error: Magic RPC Error: [-32600] ', + '', + ); + _error(MagicErrorEvent(message)); + _getSocialRedirectUri.complete(null); + } + if (messageData.connectSocialError) { + String? message = messageData.payload?['message']?.toString(); + message = message?.replaceFirst( + 'Error: Magic RPC Error: [-32600] ', + '', + ); + _error(MagicErrorEvent(message)); + _connectSocial.complete(false); + } if (messageData.getUserError) { _error(GetUserErrorEvent()); } @@ -682,9 +730,10 @@ class MagicService implements IMagicService { } if (Platform.isAndroid) { if (_webViewController.platform is AndroidWebViewController) { + final platform = + _webViewController.platform as AndroidWebViewController; AndroidWebViewController.enableDebugging(true); - (_webViewController.platform as AndroidWebViewController) - .setMediaPlaybackRequiresUserGesture(false); + platform.setMediaPlaybackRequiresUserGesture(false); final cookieManager = WebViewCookieManager().platform as AndroidWebViewCookieManager; diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart index 739e7ae..75edb0f 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_data.dart @@ -2,9 +2,12 @@ import 'package:collection/collection.dart'; import 'package:reown_appkit/reown_appkit.dart'; class MagicData { - String email; + String? email; String address; int chainId; + String? userName; + bool? smartAccountDeployed; + String? preferredAccountType; ConnectionMetadata? self; ConnectionMetadata? peer; AppKitSocialOption? provider; @@ -13,6 +16,9 @@ class MagicData { required this.email, required this.chainId, required this.address, + this.userName, + this.smartAccountDeployed, + this.preferredAccountType, this.self, this.peer, this.provider, @@ -20,9 +26,12 @@ class MagicData { factory MagicData.fromJson(Map json) { return MagicData( - email: json['email'].toString(), + email: json['email']?.toString(), address: json['address'].toString(), chainId: int.parse(json['chainId'].toString()), + userName: json['userName']?.toString(), + smartAccountDeployed: json['smartAccountDeployed'] as bool?, + preferredAccountType: json['preferredAccountType']?.toString(), self: (json['self'] != null) ? ConnectionMetadata.fromJson(json['self']) : null, @@ -41,6 +50,9 @@ class MagicData { 'email': email, 'address': address, 'chainId': chainId, + 'userName': userName, + 'smartAccountDeployed': smartAccountDeployed, + 'preferredAccountType': preferredAccountType, 'self': self?.toJson(), 'peer': peer?.toJson(), 'provider': provider?.name.toLowerCase(), @@ -54,6 +66,9 @@ class MagicData { String? email, String? address, int? chainId, + String? userName, + bool? smartAccountDeployed, + String? preferredAccountType, ConnectionMetadata? self, ConnectionMetadata? peer, AppKitSocialOption? provider, @@ -62,6 +77,9 @@ class MagicData { email: email ?? this.email, address: address ?? this.address, chainId: chainId ?? this.chainId, + userName: userName ?? this.userName, + smartAccountDeployed: smartAccountDeployed ?? this.smartAccountDeployed, + preferredAccountType: preferredAccountType ?? this.preferredAccountType, self: self ?? this.self, peer: peer ?? this.peer, provider: provider ?? this.provider, diff --git a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart index 2e812f1..254ae99 100644 --- a/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart +++ b/packages/reown_appkit/lib/modal/services/magic_service/models/magic_events.dart @@ -122,3 +122,8 @@ class RpcRequestErrorEvent extends MagicErrorEvent { RpcRequestErrorEvent(String? message) : super(message ?? 'Error during request'); } + +// class SocialLoginEvent implements EventArgs { +// final String? uri; +// SocialLoginEvent(this.uri); +// } diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart b/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart index 85d0396..34ca8a0 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/email_login_input_field.dart @@ -42,7 +42,7 @@ class _EmailLoginInputFieldState extends State { @override Widget build(BuildContext context) { return ValueListenableBuilder( - valueListenable: magicService.instance.isEnabled, + valueListenable: magicService.instance.isEmailEnabled, builder: (context, emailEnabled, _) { if (!emailEnabled) { return const SizedBox.shrink(); diff --git a/packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart b/packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart index 8d566ef..fffd6e4 100644 --- a/packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart +++ b/packages/reown_appkit/lib/modal/widgets/buttons/social_login_buttons_view.dart @@ -3,12 +3,15 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:reown_appkit/modal/constants/style_constants.dart'; +import 'package:reown_appkit/modal/pages/all_social_logins.dart'; import 'package:reown_appkit/modal/pages/social_login_page.dart'; import 'package:reown_appkit/modal/services/magic_service/magic_service_singleton.dart'; import 'package:reown_appkit/modal/utils/asset_util.dart'; import 'package:reown_appkit/modal/widgets/lists/list_items/base_list_item.dart'; +import 'package:reown_appkit/modal/widgets/miscellaneous/responsive_container.dart'; import 'package:reown_appkit/modal/widgets/widget_stack/widget_stack_singleton.dart'; import 'package:reown_appkit/reown_appkit.dart'; +import 'package:shimmer/shimmer.dart'; class SocialLoginButtonsView extends StatefulWidget { const SocialLoginButtonsView({super.key}); @@ -22,96 +25,178 @@ class _SocialLoginButtonsViewState extends State { Widget build(BuildContext context) { final themeData = ReownAppKitModalTheme.getDataOf(context); final themeColors = ReownAppKitModalTheme.colorsOf(context); + final isPortrait = ResponsiveData.isPortrait(context); return ValueListenableBuilder( valueListenable: magicService.instance.isReady, - builder: (context, value, _) { + builder: (context, isReady, _) { final socialButtons = magicService.instance.socials; final count = socialButtons.length; if (count == 0) { return SizedBox.shrink(); } - final fits = count == 6; - final exceeds = count > 6; + final maxItems = isPortrait ? 6 : 8; + final fits = count == maxItems; + final exceeds = count > maxItems; // final firstItem = socialButtons.first; final restItems = fits - ? socialButtons.sublist(1, min(socialButtons.length, 6)) - : socialButtons.sublist(1, min(socialButtons.length, 5)); - + ? socialButtons.sublist(1, min(socialButtons.length, maxItems)) + : socialButtons.sublist( + 1, min(socialButtons.length, (maxItems - 1))); + final radiuses = ReownAppKitModalTheme.radiusesOf(context); final secondRowList = [ ...restItems.map( (item) => Expanded( - child: BaseListItem( - onTap: value ? () => _initSocialLogin(item) : null, - child: LayoutBuilder( - builder: (_, constraints) { - return SvgPicture.asset( - AssetUtils.getThemedAsset( - context, - '${item.name.toLowerCase()}_logo.svg', + child: isReady + ? BaseListItem( + onTap: () => _initSocialLogin(item), + child: LayoutBuilder( + builder: (_, constraints) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ClipRRect( + borderRadius: radiuses.isSquare() + ? BorderRadius.zero + : BorderRadius.circular( + constraints.maxHeight), + child: SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${item.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + height: constraints.maxHeight, + width: constraints.maxHeight, + ), + ), + ], + ); + }, ), - package: 'reown_appkit', - height: constraints.maxHeight, - width: constraints.maxHeight, - ); - }, - ), - ), + ) + : Shimmer.fromColors( + baseColor: themeColors.grayGlass005, + highlightColor: themeColors.grayGlass020, + child: BaseListItem( + child: LayoutBuilder( + builder: (_, constraints) => CircleAvatar( + radius: constraints.maxHeight / 2, + ), + ), + ), + ), ), ), if (exceeds) Expanded( - child: BaseListItem( - // TODO Social Login - // onTap: value ? () => _initSocialLogin() : null, - child: LayoutBuilder( - builder: (_, constraints) { - return SvgPicture.asset( - AssetUtils.getThemedAsset( - context, - 'more_social_icon.svg', + child: isReady + ? BaseListItem( + onTap: () => widgetStack.instance.push( + AllSocialLoginsPage( + onSelect: (selected) { + widgetStack.instance.pop(); + _initSocialLogin(selected); + }, + ), ), - package: 'reown_appkit', - height: constraints.maxHeight, - width: constraints.maxHeight, - ); - }, - ), - ), + child: LayoutBuilder( + builder: (_, constraints) { + return ClipRRect( + borderRadius: radiuses.isSquare() + ? BorderRadius.zero + : BorderRadius.circular(constraints.maxHeight), + child: SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + 'more_social_icon.svg', + ), + package: 'reown_appkit', + height: constraints.maxHeight, + width: constraints.maxHeight, + ), + ); + }, + ), + ) + : Shimmer.fromColors( + baseColor: themeColors.grayGlass005, + highlightColor: themeColors.grayGlass020, + child: BaseListItem( + child: LayoutBuilder( + builder: (_, constraints) => CircleAvatar( + radius: constraints.maxHeight / 2, + ), + ), + ), + ), ), ]; return Column( children: [ const SizedBox.square(dimension: kListViewSeparatorHeight), - BaseListItem( - onTap: value ? () => _initSocialLogin(firstItem) : null, - child: Row( - children: [ - LayoutBuilder( - builder: (_, constraints) { - return SvgPicture.asset( - AssetUtils.getThemedAsset( - context, - '${firstItem.name.toLowerCase()}_logo.svg', + isReady + ? BaseListItem( + onTap: () => _initSocialLogin(firstItem), + child: Row( + children: [ + LayoutBuilder( + builder: (_, constraints) { + return ClipRRect( + borderRadius: radiuses.isSquare() + ? BorderRadius.zero + : BorderRadius.circular( + constraints.maxHeight), + child: SvgPicture.asset( + AssetUtils.getThemedAsset( + context, + '${firstItem.name.toLowerCase()}_logo.svg', + ), + package: 'reown_appkit', + height: constraints.maxHeight, + width: constraints.maxHeight, + ), + ); + }, ), - package: 'reown_appkit', - height: constraints.maxHeight, - width: constraints.maxHeight, - ); - }, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Text( - 'Continue with ${firstItem.name}', - style: themeData.textStyles.paragraph500.copyWith( - color: themeColors.foreground100, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Text( + 'Continue with ${firstItem.name}', + style: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + ), + ), + ], + ), + ) + : Shimmer.fromColors( + baseColor: themeColors.grayGlass005, + highlightColor: themeColors.grayGlass020, + child: BaseListItem( + child: Row( + children: [ + const SizedBox.square(dimension: 4.0), + LayoutBuilder( + builder: (_, constraints) => CircleAvatar( + radius: constraints.maxHeight / 2, + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0), + child: Text( + 'Continue with ${firstItem.name}', + style: themeData.textStyles.paragraph500.copyWith( + color: themeColors.foreground100, + ), + ), + ), + ], ), ), ), - ], - ), - ), Column( children: [ const SizedBox.square(dimension: kListViewSeparatorHeight), diff --git a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_connect_button.dart b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_connect_button.dart index 0707804..77b1011 100644 --- a/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_connect_button.dart +++ b/packages/reown_appkit/lib/modal/widgets/public/appkit_modal_connect_button.dart @@ -134,8 +134,9 @@ class _WebViewWidgetState extends State<_WebViewWidget> { @override Widget build(BuildContext context) { - final emailEnabled = magicService.instance.isEnabled.value; - if (emailEnabled && _show) { + final emailEnabled = magicService.instance.isEmailEnabled.value; + final socialEnabled = magicService.instance.isSocialEnabled.value; + if ((emailEnabled || socialEnabled) && _show) { return SizedBox( width: 0.5, height: 0.5, diff --git a/packages/reown_appkit/lib/version.dart b/packages/reown_appkit/lib/version.dart index 92a0d66..cf2dd1f 100644 --- a/packages/reown_appkit/lib/version.dart +++ b/packages/reown_appkit/lib/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '1.0.1'; +const packageVersion = '1.1.0'; diff --git a/packages/reown_appkit/pubspec.yaml b/packages/reown_appkit/pubspec.yaml index 1b4bdcd..55f4171 100644 --- a/packages/reown_appkit/pubspec.yaml +++ b/packages/reown_appkit/pubspec.yaml @@ -1,6 +1,6 @@ name: reown_appkit description: "Reown is the onchain UX platform that provides toolkits built on top of the WalletConnect Network" -version: 1.0.1 +version: 1.1.0 homepage: https://github.com/reown-com/reown_flutter repository: https://github.com/reown-com/reown_flutter/tree/master/packages/reown_appkit documentation: https://docs.reown.com/appkit/flutter/core/installation diff --git a/packages/reown_walletkit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/reown_walletkit/example/ios/Runner.xcodeproj/project.pbxproj index 84a1ca5..716d2f3 100644 --- a/packages/reown_walletkit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/reown_walletkit/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,12 +9,12 @@ /* Begin PBXBuildFile section */ 091D8F542C4A7A5000904D6C /* Info-internal.plist in Resources */ = {isa = PBXBuildFile; fileRef = 091D8F532C4A7A5000904D6C /* Info-internal.plist */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2210E862B6357BBBE637E65E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B30D38A088691AB65C417E1 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + DEFE8343A68EB301FB92A032 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC62DA35D3B5AEEE24270E90 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -31,17 +31,17 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 079D03AE5B5980595BF548F5 /* Pods-Runner.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-production.xcconfig"; sourceTree = ""; }; 091D8F532C4A7A5000904D6C /* Info-internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-internal.plist"; sourceTree = ""; }; 0978D7E02C6B682E00E3593C /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2D936F9BF00E9197AECC09BE /* Pods-Runner.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-production.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4344D4580FDF616D112FD695 /* Pods-Runner.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-production.xcconfig"; sourceTree = ""; }; - 6C9EDDA5952C970914CCB47F /* Pods-Runner.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-production.xcconfig"; sourceTree = ""; }; + 62E839944AC7715806C90DEA /* Pods-Runner.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-production.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8B30D38A088691AB65C417E1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -49,10 +49,10 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9AA202A4F9D07A1A38223F79 /* Pods-Runner.debug-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-internal.xcconfig"; sourceTree = ""; }; - C375F5AA7036C24F3F188FA4 /* Pods-Runner.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-internal.xcconfig"; sourceTree = ""; }; - FB97E2D3A1A3B24E657AF4BE /* Pods-Runner.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-production.xcconfig"; sourceTree = ""; }; - FCEC095E30F1D60B46295232 /* Pods-Runner.profile-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-internal.xcconfig"; sourceTree = ""; }; + A12E51DAE37CC01211A8FAA4 /* Pods-Runner.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-internal.xcconfig"; sourceTree = ""; }; + AC62DA35D3B5AEEE24270E90 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B5CBF0162267C8C160278126 /* Pods-Runner.debug-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-internal.xcconfig"; sourceTree = ""; }; + DA9B56C90113F1EBF75BB68D /* Pods-Runner.profile-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-internal.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -60,7 +60,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2210E862B6357BBBE637E65E /* Pods_Runner.framework in Frameworks */, + DEFE8343A68EB301FB92A032 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -85,7 +85,7 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, C4281BF2C021B5570082F0DC /* Pods */, - B0BA46C205DD193AD1539395 /* Frameworks */, + FEEBBBAFC3DD65FF62DEDFDA /* Frameworks */, ); sourceTree = ""; }; @@ -114,25 +114,25 @@ path = Runner; sourceTree = ""; }; - B0BA46C205DD193AD1539395 /* Frameworks */ = { + C4281BF2C021B5570082F0DC /* Pods */ = { isa = PBXGroup; children = ( - 8B30D38A088691AB65C417E1 /* Pods_Runner.framework */, + 2D936F9BF00E9197AECC09BE /* Pods-Runner.debug-production.xcconfig */, + B5CBF0162267C8C160278126 /* Pods-Runner.debug-internal.xcconfig */, + 62E839944AC7715806C90DEA /* Pods-Runner.release-production.xcconfig */, + A12E51DAE37CC01211A8FAA4 /* Pods-Runner.release-internal.xcconfig */, + 079D03AE5B5980595BF548F5 /* Pods-Runner.profile-production.xcconfig */, + DA9B56C90113F1EBF75BB68D /* Pods-Runner.profile-internal.xcconfig */, ); - name = Frameworks; + path = Pods; sourceTree = ""; }; - C4281BF2C021B5570082F0DC /* Pods */ = { + FEEBBBAFC3DD65FF62DEDFDA /* Frameworks */ = { isa = PBXGroup; children = ( - 6C9EDDA5952C970914CCB47F /* Pods-Runner.debug-production.xcconfig */, - 9AA202A4F9D07A1A38223F79 /* Pods-Runner.debug-internal.xcconfig */, - 4344D4580FDF616D112FD695 /* Pods-Runner.release-production.xcconfig */, - C375F5AA7036C24F3F188FA4 /* Pods-Runner.release-internal.xcconfig */, - FB97E2D3A1A3B24E657AF4BE /* Pods-Runner.profile-production.xcconfig */, - FCEC095E30F1D60B46295232 /* Pods-Runner.profile-internal.xcconfig */, + AC62DA35D3B5AEEE24270E90 /* Pods_Runner.framework */, ); - path = Pods; + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ @@ -142,14 +142,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 253EC1671D118E0514180759 /* [CP] Check Pods Manifest.lock */, + F93EBB2F818AB3E9B58529CE /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 33F8A66AB48275C47BFAE6E6 /* [CP] Embed Pods Frameworks */, + 653885DCAF153E5494F95AD7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -209,29 +209,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 253EC1671D118E0514180759 /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); + name = "Thin Binary"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 33F8A66AB48275C47BFAE6E6 /* [CP] Embed Pods Frameworks */ = { + 653885DCAF153E5494F95AD7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -248,36 +242,42 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); - name = "Thin Binary"; + name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + F93EBB2F818AB3E9B58529CE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Run Script"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ From 47badca2580a5826b721a7125b3188e3a3a8ecf3 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Mon, 30 Sep 2024 12:15:41 +0200 Subject: [PATCH 06/66] social login enhancements --- .../release_dapp_android_internal.yml | 2 +- .../workflows/release_dapp_ios_internal.yml | 2 +- .../base/ios/Runner.xcodeproj/project.pbxproj | 98 +++++----- .../reown_appkit/example/base/lib/main.dart | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 101 +++++------ .../lib/modal/appkit_modal_impl.dart | 15 +- .../lib/modal/assets/dark/apple_logo.svg | 44 +++-- .../lib/modal/assets/dark/discord_logo.svg | 47 +++-- .../lib/modal/assets/dark/facebook_logo.svg | 64 ++++--- .../lib/modal/assets/dark/farcaster_logo.svg | 53 ++++-- .../lib/modal/assets/dark/github_logo.svg | 44 +++-- .../lib/modal/assets/dark/x_logo.svg | 38 ++-- .../lib/modal/assets/light/farcaster_logo.svg | 53 ++++-- .../lib/modal/assets/png/2.0x/farcaster.png | Bin 0 -> 56069 bytes .../lib/modal/assets/png/3.0x/farcaster.png | Bin 0 -> 57545 bytes .../lib/modal/assets/png/farcaster.png | Bin 0 -> 24624 bytes .../lib/modal/constants/key_constants.dart | 1 + .../models/public/appkit_modal_session.dart | 2 + .../lib/modal/pages/account_page.dart | 15 +- .../lib/modal/pages/all_social_logins.dart | 44 +---- .../modal/pages/farcaster_qrcode_page.dart | 145 +++++++++++++++ .../lib/modal/pages/social_login_page.dart | 120 +++++++++---- .../magic_service/i_magic_service.dart | 7 +- .../services/magic_service/magic_service.dart | 105 ++++++++--- .../magic_service/models/frame_message.dart | 23 ++- .../magic_service/models/magic_events.dart | 5 + .../modal/widgets/buttons/primary_button.dart | 13 +- .../widgets/buttons/social_login_button.dart | 105 +++++++++++ .../buttons/social_login_buttons_view.dart | 167 ++++-------------- .../widgets/widget_stack/i_widget_stack.dart | 1 + .../widgets/widget_stack/widget_stack.dart | 4 + packages/reown_appkit/pubspec.yaml | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 56 +++--- 33 files changed, 886 insertions(+), 490 deletions(-) create mode 100644 packages/reown_appkit/lib/modal/assets/png/2.0x/farcaster.png create mode 100644 packages/reown_appkit/lib/modal/assets/png/3.0x/farcaster.png create mode 100644 packages/reown_appkit/lib/modal/assets/png/farcaster.png create mode 100644 packages/reown_appkit/lib/modal/pages/farcaster_qrcode_page.dart create mode 100644 packages/reown_appkit/lib/modal/widgets/buttons/social_login_button.dart diff --git a/.github/workflows/release_dapp_android_internal.yml b/.github/workflows/release_dapp_android_internal.yml index e3ade37..0838a7c 100644 --- a/.github/workflows/release_dapp_android_internal.yml +++ b/.github/workflows/release_dapp_android_internal.yml @@ -8,7 +8,7 @@ on: jobs: build: - if: (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop') || github.event_name == 'workflow_dispatch' + # if: (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop') || github.event_name == 'workflow_dispatch' runs-on: macos-latest-xlarge steps: diff --git a/.github/workflows/release_dapp_ios_internal.yml b/.github/workflows/release_dapp_ios_internal.yml index 9eb7571..5cf99b5 100644 --- a/.github/workflows/release_dapp_ios_internal.yml +++ b/.github/workflows/release_dapp_ios_internal.yml @@ -13,7 +13,7 @@ on: jobs: build: - if: (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop') || github.event_name == 'workflow_dispatch' + # if: (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop') || github.event_name == 'workflow_dispatch' runs-on: macos-latest-xlarge steps: diff --git a/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj b/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj index 6924d25..6f261d5 100644 --- a/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/reown_appkit/example/base/ios/Runner.xcodeproj/project.pbxproj @@ -9,12 +9,12 @@ /* Begin PBXBuildFile section */ 0964B3132C49545400AE1CDA /* Info-internal.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0964B3122C49545400AE1CDA /* Info-internal.plist */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 1B5DEA3E6EDC27B89AF2AB05 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B56F69AB969AA03109DA32E /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + E2A4BBDE6563F357DC87FCBF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA8ADA195AC6BFC770376AA6 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -31,6 +31,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 005FB4ED91D1FD7F1E80AA6E /* Pods-Runner.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-production.xcconfig"; sourceTree = ""; }; 0964B30E2C494D2000AE1CDA /* Debug-production.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Debug-production.xcconfig"; path = "Flutter/Debug-production.xcconfig"; sourceTree = ""; }; 0964B30F2C494D2000AE1CDA /* Release-internal.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Release-internal.xcconfig"; path = "Flutter/Release-internal.xcconfig"; sourceTree = ""; }; 0964B3102C494D2000AE1CDA /* Debug-internal.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Debug-internal.xcconfig"; path = "Flutter/Debug-internal.xcconfig"; sourceTree = ""; }; @@ -39,14 +40,12 @@ 09969A8C2C73BC9100B14363 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1AC0B430DCF6F62273D6DBA7 /* Pods-Runner.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-internal.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 5830A10298A0E37E0DEACD59 /* Pods-Runner.profile-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-internal.xcconfig"; sourceTree = ""; }; - 66390723572B01E4BFAD632E /* Pods-Runner.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-internal.xcconfig"; sourceTree = ""; }; - 6B56F69AB969AA03109DA32E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8B9D10731A69F7B3B5726C07 /* Pods-Runner.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-production.xcconfig"; sourceTree = ""; }; + 8BFC12A02BF2F3AD1EDD9CB8 /* Pods-Runner.profile-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-internal.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -54,9 +53,10 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B275039F2F44BBC2812C49D6 /* Pods-Runner.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-production.xcconfig"; sourceTree = ""; }; - C87ABC9AD580EC1F17A8A628 /* Pods-Runner.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-production.xcconfig"; sourceTree = ""; }; - E6BE4CB718F715BF8881756E /* Pods-Runner.debug-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-internal.xcconfig"; sourceTree = ""; }; + 9FED920F7C3A45B54DF18D9A /* Pods-Runner.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-production.xcconfig"; sourceTree = ""; }; + B9943A2F210164D65D7E9943 /* Pods-Runner.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-production.xcconfig"; sourceTree = ""; }; + BA8ADA195AC6BFC770376AA6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E4BD8567F73E34406C03C3A6 /* Pods-Runner.debug-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-internal.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -64,7 +64,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1B5DEA3E6EDC27B89AF2AB05 /* Pods_Runner.framework in Frameworks */, + E2A4BBDE6563F357DC87FCBF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -74,24 +74,16 @@ 337F646810BF58BC9EACB5E5 /* Pods */ = { isa = PBXGroup; children = ( - 8B9D10731A69F7B3B5726C07 /* Pods-Runner.debug-production.xcconfig */, - E6BE4CB718F715BF8881756E /* Pods-Runner.debug-internal.xcconfig */, - B275039F2F44BBC2812C49D6 /* Pods-Runner.release-production.xcconfig */, - 66390723572B01E4BFAD632E /* Pods-Runner.release-internal.xcconfig */, - C87ABC9AD580EC1F17A8A628 /* Pods-Runner.profile-production.xcconfig */, - 5830A10298A0E37E0DEACD59 /* Pods-Runner.profile-internal.xcconfig */, + 9FED920F7C3A45B54DF18D9A /* Pods-Runner.debug-production.xcconfig */, + E4BD8567F73E34406C03C3A6 /* Pods-Runner.debug-internal.xcconfig */, + 005FB4ED91D1FD7F1E80AA6E /* Pods-Runner.release-production.xcconfig */, + 1AC0B430DCF6F62273D6DBA7 /* Pods-Runner.release-internal.xcconfig */, + B9943A2F210164D65D7E9943 /* Pods-Runner.profile-production.xcconfig */, + 8BFC12A02BF2F3AD1EDD9CB8 /* Pods-Runner.profile-internal.xcconfig */, ); path = Pods; sourceTree = ""; }; - 7B93B28FA9C10A6CB6A5BA08 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 6B56F69AB969AA03109DA32E /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -114,7 +106,7 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 337F646810BF58BC9EACB5E5 /* Pods */, - 7B93B28FA9C10A6CB6A5BA08 /* Frameworks */, + C369347667AD101A270A8839 /* Frameworks */, ); sourceTree = ""; }; @@ -143,6 +135,14 @@ path = Runner; sourceTree = ""; }; + C369347667AD101A270A8839 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BA8ADA195AC6BFC770376AA6 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -150,14 +150,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 36CAFCEF69924F6DF3D8A5D2 /* [CP] Check Pods Manifest.lock */, + F4E3AA2A7543A7E23A021DE8 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - C3DF71E2A734F75A9F432062 /* [CP] Embed Pods Frameworks */, + 7B56932F1A2D663AEFD1E957 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -217,43 +217,38 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 36CAFCEF69924F6DF3D8A5D2 /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); + name = "Thin Binary"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 7B56932F1A2D663AEFD1E957 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "Thin Binary"; - outputPaths = ( + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -270,21 +265,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - C3DF71E2A734F75A9F432062 /* [CP] Embed Pods Frameworks */ = { + F4E3AA2A7543A7E23A021DE8 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ diff --git a/packages/reown_appkit/example/base/lib/main.dart b/packages/reown_appkit/example/base/lib/main.dart index bfa659c..c7a375a 100644 --- a/packages/reown_appkit/example/base/lib/main.dart +++ b/packages/reown_appkit/example/base/lib/main.dart @@ -140,6 +140,7 @@ class _MyHomePageState extends State { AppKitSocialOption.X, AppKitSocialOption.Facebook, AppKitSocialOption.Github, + AppKitSocialOption.Farcaster, ], // requiredNamespaces: {}, // optionalNamespaces: {}, diff --git a/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj b/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj index b58df8d..f2dc8da 100644 --- a/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/reown_appkit/example/modal/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 516D0608705F0BE97FC929E4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D119754EA0AC5CBD4B950C40 /* Pods_Runner.framework */; }; - 6561844AD097F2CFC6B48E58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F80C0F0A2865C918627E1584 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + C31D7EF6EFC3F5349C210F65 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C67DDB0CAFEFEFEA5D9CE4C4 /* Pods_RunnerTests.framework */; }; + DE3F5AEEE8694C1DF9561053 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F45223A82C1842B462B12593 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -45,11 +45,11 @@ 092D151B2ABD988600C69848 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 26C1413BE27E5DC446084413 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 61E23A67BDA67FF5359D9357 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 4F2EF9C2AF36DD4EEFA663AE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 67DA9689C0C2E99AF3A44E60 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -60,12 +60,12 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A169B40BA5CA19BC0389255C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - B5705635CD688D31EE6A40E6 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - BB3CF3338CE662C91545CB38 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - CECA027B1D7BD08002F6D591 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - D119754EA0AC5CBD4B950C40 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F80C0F0A2865C918627E1584 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B58EDEAB5B4CF37F99743A14 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + C67DDB0CAFEFEFEA5D9CE4C4 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F3828B9F31DA1A2B61289405 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F45223A82C1842B462B12593 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F82CCAF7B11EFECAF0BA043A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + FF4DDA3CDC8BC75C8A00DB72 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -73,7 +73,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6561844AD097F2CFC6B48E58 /* Pods_RunnerTests.framework in Frameworks */, + C31D7EF6EFC3F5349C210F65 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -81,7 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 516D0608705F0BE97FC929E4 /* Pods_Runner.framework in Frameworks */, + DE3F5AEEE8694C1DF9561053 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -115,7 +115,7 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, E880F334C300E1CEFAFD6E4C /* Pods */, - C37C162CA8492E9BE105CA16 /* Frameworks */, + D170F7D69B85FFC0C1021DA7 /* Frameworks */, ); sourceTree = ""; }; @@ -144,11 +144,11 @@ path = Runner; sourceTree = ""; }; - C37C162CA8492E9BE105CA16 /* Frameworks */ = { + D170F7D69B85FFC0C1021DA7 /* Frameworks */ = { isa = PBXGroup; children = ( - D119754EA0AC5CBD4B950C40 /* Pods_Runner.framework */, - F80C0F0A2865C918627E1584 /* Pods_RunnerTests.framework */, + F45223A82C1842B462B12593 /* Pods_Runner.framework */, + C67DDB0CAFEFEFEA5D9CE4C4 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -156,12 +156,12 @@ E880F334C300E1CEFAFD6E4C /* Pods */ = { isa = PBXGroup; children = ( - 61E23A67BDA67FF5359D9357 /* Pods-Runner.debug.xcconfig */, - A169B40BA5CA19BC0389255C /* Pods-Runner.release.xcconfig */, - BB3CF3338CE662C91545CB38 /* Pods-Runner.profile.xcconfig */, - CECA027B1D7BD08002F6D591 /* Pods-RunnerTests.debug.xcconfig */, - B5705635CD688D31EE6A40E6 /* Pods-RunnerTests.release.xcconfig */, - 26C1413BE27E5DC446084413 /* Pods-RunnerTests.profile.xcconfig */, + F82CCAF7B11EFECAF0BA043A /* Pods-Runner.debug.xcconfig */, + F3828B9F31DA1A2B61289405 /* Pods-Runner.release.xcconfig */, + 4F2EF9C2AF36DD4EEFA663AE /* Pods-Runner.profile.xcconfig */, + 67DA9689C0C2E99AF3A44E60 /* Pods-RunnerTests.debug.xcconfig */, + FF4DDA3CDC8BC75C8A00DB72 /* Pods-RunnerTests.release.xcconfig */, + B58EDEAB5B4CF37F99743A14 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -173,7 +173,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 70C2F9C0A5F8998280E23BFC /* [CP] Check Pods Manifest.lock */, + 685E9B818FA4041C1280A167 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 6D09732E2619FACF7894A4A1 /* Frameworks */, @@ -192,14 +192,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 06D78A8D11118782BCBF4EA7 /* [CP] Check Pods Manifest.lock */, + 96E20722CF6D77140352C7B7 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - BD4CB75C11799DFB8AB18650 /* [CP] Embed Pods Frameworks */, + F18A29672255B97A3B56C7A1 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -270,45 +270,45 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 06D78A8D11118782BCBF4EA7 /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); + name = "Thin Binary"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 685E9B818FA4041C1280A167 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Thin Binary"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - 70C2F9C0A5F8998280E23BFC /* [CP] Check Pods Manifest.lock */ = { + 96E20722CF6D77140352C7B7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -323,7 +323,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -345,7 +345,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - BD4CB75C11799DFB8AB18650 /* [CP] Embed Pods Frameworks */ = { + F18A29672255B97A3B56C7A1 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -493,7 +493,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = CECA027B1D7BD08002F6D591 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 67DA9689C0C2E99AF3A44E60 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -511,7 +511,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B5705635CD688D31EE6A40E6 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = FF4DDA3CDC8BC75C8A00DB72 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -527,7 +527,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 26C1413BE27E5DC446084413 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = B58EDEAB5B4CF37F99743A14 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -656,11 +656,9 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 73; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; + DEVELOPMENT_TEAM = W5R8AG9K22; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -671,7 +669,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.web3modal.flutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.web3modal.flutterExample"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; diff --git a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart index a0a70e6..f57950b 100644 --- a/packages/reown_appkit/lib/modal/appkit_modal_impl.dart +++ b/packages/reown_appkit/lib/modal/appkit_modal_impl.dart @@ -1024,9 +1024,9 @@ class ReownAppKitModal with ChangeNotifier implements IReownAppKitModal { return; } _isOpen = false; + final currentKey = widgetStack.instance.getCurrent().key; if (_disconnectOnClose) { _disconnectOnClose = false; - final currentKey = widgetStack.instance.getCurrent().key; if (currentKey == KeyConstants.approveSiwePageKey) { analyticsService.instance.sendEvent(ClickCancelSiwe( network: _currentSelectedChainId ?? '', @@ -1613,11 +1613,13 @@ extension _EmailConnectorExtension on ReownAppKitModal { final newChainId = _getStoredChainId('${args.data!.chainId}')!; _currentSelectedChainId = newChainId; // + final email = args.data?.email ?? _currentSession?.toRawJson()['email']; + final userName = + args.data?.userName ?? _currentSession?.toRawJson()['userName']; final magicData = args.data?.copytWith( chainId: int.tryParse(newChainId), - email: args.data?.email ?? _currentSession?.toRawJson()['email'], - userName: - args.data?.userName ?? _currentSession?.toRawJson()['userName'], + email: email, + userName: userName, ); final session = ReownAppKitModalSession(magicData: magicData); @@ -1652,7 +1654,9 @@ extension _EmailConnectorExtension on ReownAppKitModal { _logger.d('[$runtimeType] _onMagicUpdateEvent: $args'); if (args != null) { try { - final newEmail = args.email ?? _currentSession!.email; + final userName = _currentSession?.toRawJson()['userName']; + final email = _currentSession?.toRawJson()['email']; + final newEmail = args.email ?? email ?? userName; final address = args.address ?? _currentSession!.address!; final chainId = args.chainId?.toString() ?? _currentSession!.chainId; _currentSelectedChainId = chainId; @@ -1661,6 +1665,7 @@ extension _EmailConnectorExtension on ReownAppKitModal { email: newEmail, address: address, chainId: int.parse(chainId), + userName: userName, // peer: _currentSession?.peer, // self: _currentSession?.self, ); diff --git a/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg index b32ec0d..b9c0a52 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/apple_logo.svg @@ -1,18 +1,28 @@ - - - - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg index c1297ca..d3e8e7d 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/discord_logo.svg @@ -1,17 +1,32 @@ - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg index 36d6d69..e100f2b 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/facebook_logo.svg @@ -1,26 +1,40 @@ - - - - - - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg index 28c641e..6c82bf8 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/farcaster_logo.svg @@ -1,14 +1,43 @@ - - - - - - - - + + + +Created with Fabric.js 5.2.4 - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg index e8f413a..66bf7fc 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/github_logo.svg @@ -1,18 +1,28 @@ - - - - - - - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg b/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg index 8fbeb50..11ff04c 100644 --- a/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/dark/x_logo.svg @@ -1,12 +1,28 @@ - - - - - - - - + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg b/packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg index 28c641e..6c82bf8 100644 --- a/packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg +++ b/packages/reown_appkit/lib/modal/assets/light/farcaster_logo.svg @@ -1,14 +1,43 @@ - - - - - - - - + + + +Created with Fabric.js 5.2.4 - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/reown_appkit/lib/modal/assets/png/2.0x/farcaster.png b/packages/reown_appkit/lib/modal/assets/png/2.0x/farcaster.png new file mode 100644 index 0000000000000000000000000000000000000000..f178425cc534db761792cecc94e20f7126aa6a35 GIT binary patch literal 56069 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 T0F1EV00000NkvXXu0mjfRhr63 literal 0 HcmV?d00001 diff --git a/packages/reown_appkit/lib/modal/assets/png/3.0x/farcaster.png b/packages/reown_appkit/lib/modal/assets/png/3.0x/farcaster.png new file mode 100644 index 0000000000000000000000000000000000000000..dd5b0ed630a6d599fdd91c5cae1f4530c86de4a0 GIT binary patch literal 57545 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