* init

* updates

* nano updates

* updates

* updates

* [skipci] wip deep link changes

* fix deep links

* minor fix

* add reminder message on buy and exchange routes

* [skip ci] font fixes

* review updates

* [skip ci] minor fix

* save

* fixes

* minor code cleanup

* minor potential fix
This commit is contained in:
Matthew Fosse
2024-05-08 03:00:01 +03:00
committed by GitHub
parent e5be737236
commit baad7f7469
46 changed files with 486 additions and 195 deletions
+85 -60
View File
@@ -26,6 +26,7 @@ import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cw_core/receive_page_option.dart';
@@ -268,6 +269,7 @@ Future<void> setup({
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource,
required FlutterSecureStorage secureStorage,
required GlobalKey<NavigatorState> navigatorKey,
}) async {
_walletInfoSource = walletInfoSource;
_nodeSource = nodeSource;
@@ -429,68 +431,89 @@ Future<void> setup({
),
);
getIt.registerFactory<AuthPage>(() {
return AuthPage(getIt.get<AuthViewModel>(),
getIt.registerLazySingleton<LinkViewModel>(() {
return LinkViewModel(
appStore: getIt.get<AppStore>(),
settingsStore: getIt.get<SettingsStore>(),
authenticationStore: getIt.get<AuthenticationStore>(),
navigatorKey: navigatorKey,
);
});
getIt.registerFactory<AuthPage>(instanceName: 'login', () {
return AuthPage(getIt.get<AuthViewModel>(), closable: false,
onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) {
if (!isAuthenticated) {
return;
} else {
final authStore = getIt.get<AuthenticationStore>();
final appStore = getIt.get<AppStore>();
final useTotp = appStore.settingsStore.useTOTP2FA;
final shouldUseTotp2FAToAccessWallets =
appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
if (useTotp && shouldUseTotp2FAToAccessWallets) {
authPageState.close(
route: Routes.totpAuthCodePage,
arguments: TotpAuthArgumentsModel(
isForSetup: false,
isClosable: false,
onTotpAuthenticationFinished: (bool isAuthenticatedSuccessfully,
TotpAuthCodePageState totpAuthPageState) async {
if (!isAuthenticatedSuccessfully) {
return;
}
if (appStore.wallet != null) {
authStore.allowed();
return;
}
totpAuthPageState.changeProcessText('Loading the wallet');
if (loginError != null) {
totpAuthPageState.changeProcessText('ERROR: ${loginError.toString()}');
}
ReactionDisposer? _reaction;
_reaction = reaction((_) => appStore.wallet, (Object? _) {
_reaction?.reaction.dispose();
authStore.allowed();
});
},
),
);
} else {
if (appStore.wallet != null) {
authStore.allowed();
return;
}
authPageState.changeProcessText('Loading the wallet');
if (loginError != null) {
authPageState.changeProcessText('ERROR: ${loginError.toString()}');
}
ReactionDisposer? _reaction;
_reaction = reaction((_) => appStore.wallet, (Object? _) {
_reaction?.reaction.dispose();
authStore.allowed();
});
}
}
}, closable: false);
}, instanceName: 'login');
final authStore = getIt.get<AuthenticationStore>();
final appStore = getIt.get<AppStore>();
final useTotp = appStore.settingsStore.useTOTP2FA;
final shouldUseTotp2FAToAccessWallets =
appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
if (useTotp && shouldUseTotp2FAToAccessWallets) {
authPageState.close(
route: Routes.totpAuthCodePage,
arguments: TotpAuthArgumentsModel(
isForSetup: false,
isClosable: false,
onTotpAuthenticationFinished:
(bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuthPageState) async {
if (!isAuthenticatedSuccessfully) {
return;
}
if (appStore.wallet != null) {
authStore.allowed();
return;
}
totpAuthPageState.changeProcessText('Loading the wallet');
if (loginError != null) {
totpAuthPageState.changeProcessText('ERROR: ${loginError.toString()}');
}
ReactionDisposer? _reaction;
_reaction = reaction((_) => appStore.wallet, (Object? _) {
_reaction?.reaction.dispose();
authStore.allowed();
});
},
),
);
} else {
// wallet is already loaded:
if (appStore.wallet != null) {
// goes to the dashboard:
authStore.allowed();
// trigger any deep links:
final linkViewModel = getIt.get<LinkViewModel>();
if (linkViewModel.currentLink != null) {
linkViewModel.handleLink();
}
return;
}
// load the wallet:
authPageState.changeProcessText('Loading the wallet');
if (loginError != null) {
authPageState.changeProcessText('ERROR: ${loginError.toString()}');
}
ReactionDisposer? _reaction;
_reaction = reaction((_) => appStore.wallet, (Object? _) {
_reaction?.reaction.dispose();
authStore.allowed();
final linkViewModel = getIt.get<LinkViewModel>();
if (linkViewModel.currentLink != null) {
linkViewModel.handleLink();
}
});
}
});
});
getIt.registerSingleton<BottomSheetService>(BottomSheetServiceImpl());
@@ -849,8 +872,10 @@ Future<void> setup({
tradesStore: getIt.get<TradesStore>(),
sendViewModel: getIt.get<SendViewModel>()));
getIt.registerFactory(
() => ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>()));
getIt.registerFactoryParam<ExchangePage, PaymentRequest?, void>(
(PaymentRequest? paymentRequest, __) {
return ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>(), paymentRequest);
});
getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get<TradesStore>()));
+17 -12
View File
@@ -7,6 +7,7 @@ import 'package:cake_wallet/locales/locale.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cw_core/address_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/hive_type_ids.dart';
@@ -205,18 +206,20 @@ Future<void> initialSetup(
nodes: nodes,
powNodes: powNodes);
await setup(
walletInfoSource: walletInfoSource,
nodeSource: nodes,
powNodeSource: powNodes,
contactSource: contactSource,
tradesSource: tradesSource,
templates: templates,
exchangeTemplates: exchangeTemplates,
transactionDescriptionBox: transactionDescriptions,
ordersSource: ordersSource,
anonpayInvoiceInfoSource: anonpayInvoiceInfo,
unspentCoinsInfoSource: unspentCoinsInfoSource,
secureStorage: secureStorage);
walletInfoSource: walletInfoSource,
nodeSource: nodes,
powNodeSource: powNodes,
contactSource: contactSource,
tradesSource: tradesSource,
templates: templates,
exchangeTemplates: exchangeTemplates,
transactionDescriptionBox: transactionDescriptions,
ordersSource: ordersSource,
anonpayInvoiceInfoSource: anonpayInvoiceInfo,
unspentCoinsInfoSource: unspentCoinsInfoSource,
secureStorage: secureStorage,
navigatorKey: navigatorKey,
);
await bootstrap(navigatorKey);
monero?.onStartup();
}
@@ -287,6 +290,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
return Observer(builder: (BuildContext context) {
final appStore = getIt.get<AppStore>();
final authService = getIt.get<AuthService>();
final linkViewModel = getIt.get<LinkViewModel>();
final settingsStore = appStore.settingsStore;
final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get<AuthenticationStore>();
@@ -309,6 +313,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
authenticationStore: authenticationStore,
navigatorKey: navigatorKey,
authService: authService,
linkViewModel: linkViewModel,
child: MaterialApp(
navigatorObservers: [routeObserver],
navigatorKey: navigatorKey,
+11 -10
View File
@@ -221,7 +221,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
Navigator.of(context.context).pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
Navigator.of(context.context)
.pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
),
fullscreenDialog: true,
);
@@ -231,9 +232,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => ConnectDevicePage(
ConnectDevicePageParams(
walletType: availableWalletTypes.first,
onConnectDevice: (BuildContext context, _) =>
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
arguments: [availableWalletTypes.first]),
onConnectDevice: (BuildContext context, _) => Navigator.of(context).pushNamed(
Routes.chooseHardwareWalletAccount,
arguments: [availableWalletTypes.first]),
),
getIt.get<LedgerViewModel>(),
));
@@ -243,9 +244,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
param1: (BuildContext context, WalletType type) {
final arguments = ConnectDevicePageParams(
walletType: type,
onConnectDevice: (BuildContext context, _) =>
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
arguments: [type]),
onConnectDevice: (BuildContext context, _) => Navigator.of(context)
.pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
);
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
@@ -308,8 +308,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.bumpFeePage:
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) =>
getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
builder: (_) => getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
case Routes.newSubaddress:
return CupertinoPageRoute<void>(
@@ -461,7 +460,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.exchange:
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<ExchangePage>());
fullscreenDialog: true,
builder: (_) => getIt.get<ExchangePage>(param1: settings.arguments as PaymentRequest?),
);
case Routes.exchangeTemplate:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTemplatePage>());
@@ -59,12 +59,15 @@ class MarketPlacePage extends StatelessWidget {
// ),
SizedBox(height: 20),
DashBoardRoundedCardWidget(
onTap: () => launchUrl(
Uri.https("buy.cakepay.com"),
mode: LaunchMode.externalApplication,
),
title: S.of(context).cake_pay_web_cards_title,
subTitle: S.of(context).cake_pay_web_cards_subtitle,
onTap: () => _launchMarketPlaceUrl("buy.cakepay.com"),
),
const SizedBox(height: 20),
DashBoardRoundedCardWidget(
title: "NanoGPT",
subTitle: S.of(context).nanogpt_subtitle,
onTap: () => _launchMarketPlaceUrl("cake.nano-gpt.com"),
),
],
),
@@ -76,6 +79,17 @@ class MarketPlacePage extends StatelessWidget {
);
}
void _launchMarketPlaceUrl(String url) async {
try {
launchUrl(
Uri.https(url),
mode: LaunchMode.externalApplication,
);
} catch (e) {
print(e);
}
}
// TODO: Remove ionia flow/files if we will discard it
void _navigatorToGiftCardsPage(BuildContext context) {
final walletType = dashboardViewModel.type;
+9 -1
View File
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/add_template_button.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/debounce.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_type.dart';
@@ -43,7 +44,7 @@ import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
class ExchangePage extends BasePage {
ExchangePage(this.exchangeViewModel, this.authService) {
ExchangePage(this.exchangeViewModel, this.authService, this.initialPaymentRequest) {
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name
: null;
@@ -54,6 +55,7 @@ class ExchangePage extends BasePage {
final ExchangeViewModel exchangeViewModel;
final AuthService authService;
final PaymentRequest? initialPaymentRequest;
final depositKey = GlobalKey<ExchangeCardState>();
final receiveKey = GlobalKey<ExchangeCardState>();
final _formKey = GlobalKey<FormState>();
@@ -543,6 +545,12 @@ class ExchangePage extends BasePage {
// amount: depositAmountController.text);
});
if (initialPaymentRequest != null) {
exchangeViewModel.receiveCurrency = CryptoCurrency.fromString(initialPaymentRequest!.scheme);
exchangeViewModel.depositAmount = initialPaymentRequest!.amount;
exchangeViewModel.receiveAddress = initialPaymentRequest!.address;
}
_isReactionsSet = true;
}
+40 -75
View File
@@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@@ -25,6 +26,7 @@ class Root extends StatefulWidget {
required this.child,
required this.navigatorKey,
required this.authService,
required this.linkViewModel,
}) : super(key: key);
final AuthenticationStore authenticationStore;
@@ -32,6 +34,7 @@ class Root extends StatefulWidget {
final GlobalKey<NavigatorState> navigatorKey;
final AuthService authService;
final Widget child;
final LinkViewModel linkViewModel;
@override
RootState createState() => RootState();
@@ -53,7 +56,6 @@ class RootState extends State<Root> with WidgetsBindingObserver {
StreamSubscription<Uri?>? stream;
ReactionDisposer? _walletReactionDisposer;
ReactionDisposer? _deepLinksReactionDisposer;
Uri? launchUri;
@override
void initState() {
@@ -98,7 +100,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
void handleDeepLinking(Uri? uri) async {
if (uri == null || !mounted) return;
launchUri = uri;
widget.linkViewModel.currentLink = uri;
bool requireAuth = await widget.authService.requireAuth();
@@ -112,7 +114,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
(AuthenticationState state) {
if (state == AuthenticationState.allowed) {
if (widget.appStore.wallet == null) {
waitForWalletInstance(context, launchUri!);
waitForWalletInstance(context);
} else {
_navigateToDeepLinkScreen();
}
@@ -150,6 +152,8 @@ class RootState extends State<Root> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
// this only happens when the app has been in the background for some time
// this does NOT trigger when the app is started from the "closed" state!
if (_isInactive && !_postFrameCallback && _requestAuth) {
_postFrameCallback = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -158,40 +162,38 @@ class RootState extends State<Root> with WidgetsBindingObserver {
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (!isAuthenticatedSuccessfully) {
return;
}
final useTotp = widget.appStore.settingsStore.useTOTP2FA;
final shouldUseTotp2FAToAccessWallets =
widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
if (useTotp && shouldUseTotp2FAToAccessWallets) {
_reset();
auth.close(
route: Routes.totpAuthCodePage,
arguments: TotpAuthArgumentsModel(
onTotpAuthenticationFinished:
(bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) {
if (!isAuthenticatedSuccessfully) {
return;
}
_reset();
totpAuth.close(
route: widget.linkViewModel.getRouteToGo(),
arguments: widget.linkViewModel.getRouteArgs(),
);
widget.linkViewModel.currentLink = null;
},
isForSetup: false,
isClosable: false,
),
);
} else {
final useTotp = widget.appStore.settingsStore.useTOTP2FA;
final shouldUseTotp2FAToAccessWallets =
widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
if (useTotp && shouldUseTotp2FAToAccessWallets) {
_reset();
auth.close(
route: Routes.totpAuthCodePage,
arguments: TotpAuthArgumentsModel(
onTotpAuthenticationFinished:
(bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) {
if (!isAuthenticatedSuccessfully) {
return;
}
_reset();
totpAuth.close(
route: _getRouteToGo(),
arguments:
isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
);
launchUri = null;
},
isForSetup: false,
isClosable: false,
),
);
} else {
_reset();
auth.close(
route: _getRouteToGo(),
arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
);
launchUri = null;
}
_reset();
auth.close(
route: widget.linkViewModel.getRouteToGo(),
arguments: widget.linkViewModel.getRouteArgs(),
);
widget.linkViewModel.currentLink = null;
}
},
);
@@ -216,36 +218,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
_isInactiveController.add(value);
}
bool _isValidPaymentUri() => launchUri?.path.isNotEmpty ?? false;
bool get isWalletConnectLink => launchUri?.authority == 'wc';
String? _getRouteToGo() {
if (isWalletConnectLink) {
if (isEVMCompatibleChain(widget.appStore.wallet!.type)) {
_nonETHWalletErrorToast(S.current.switchToEVMCompatibleWallet);
return null;
}
return Routes.walletConnectConnectionsListing;
} else if (_isValidPaymentUri()) {
return Routes.send;
} else {
return null;
}
}
Future<void> _nonETHWalletErrorToast(String message) async {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.SNACKBAR,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 16.0,
);
}
void waitForWalletInstance(BuildContext context, Uri tempLaunchUri) {
void waitForWalletInstance(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
_walletReactionDisposer = reaction(
@@ -263,14 +236,6 @@ class RootState extends State<Root> with WidgetsBindingObserver {
}
void _navigateToDeepLinkScreen() {
if (_getRouteToGo() != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.navigatorKey.currentState?.pushNamed(
_getRouteToGo()!,
arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
);
launchUri = null;
});
}
widget.linkViewModel.handleLink();
}
}
+27 -7
View File
@@ -35,6 +35,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:url_launcher/url_launcher.dart';
class SendPage extends BasePage {
SendPage({
@@ -420,12 +422,10 @@ class SendPage extends BasePage {
}
reaction((_) => sendViewModel.state, (ExecutionState state) {
if (dialogContext != null && dialogContext?.mounted == true) {
Navigator.of(dialogContext!).pop();
}
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
@@ -460,10 +460,10 @@ class SendPage extends BasePage {
outputs: sendViewModel.outputs,
rightButtonText: S.of(_dialogContext).send,
leftButtonText: S.of(_dialogContext).cancel,
actionRightButton: () {
actionRightButton: () async {
Navigator.of(_dialogContext).pop();
sendViewModel.commitTransaction();
showPopUp<void>(
await showPopUp<void>(
context: context,
builder: (BuildContext _dialogContext) {
return Observer(builder: (_) {
@@ -481,12 +481,14 @@ class SendPage extends BasePage {
sendViewModel.selectedCryptoCurrency.toString());
final waitMessage = sendViewModel.walletType == WalletType.solana
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}' : '';
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}'
: '';
final newContactMessage = newContactAddress != null
? '\n${S.of(_dialogContext).add_contact_to_address_book}' : '';
? '\n${S.of(_dialogContext).add_contact_to_address_book}'
: '';
final alertContent =
String alertContent =
"$successMessage$waitMessage$newContactMessage";
if (newContactAddress != null) {
@@ -509,6 +511,10 @@ class SendPage extends BasePage {
newContactAddress = null;
});
} else {
if (initialPaymentRequest?.callbackMessage?.isNotEmpty ??
false) {
alertContent = initialPaymentRequest!.callbackMessage!;
}
return AlertWithOneAction(
alertTitle: '',
alertContent: alertContent,
@@ -523,6 +529,20 @@ class SendPage extends BasePage {
return Offstage();
});
});
if (state is TransactionCommitted) {
if (initialPaymentRequest?.callbackUrl?.isNotEmpty ?? false) {
// wait a second so it's not as jarring:
await Future.delayed(Duration(seconds: 1));
try {
launchUrl(
Uri.parse(initialPaymentRequest!.callbackUrl!),
mode: LaunchMode.externalApplication,
);
} catch (e) {
print(e);
}
}
}
},
actionLeftButton: () => Navigator.of(_dialogContext).pop());
});
+22 -3
View File
@@ -1,19 +1,29 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/nano/nano.dart';
class PaymentRequest {
PaymentRequest(this.address, this.amount, this.note, this.scheme);
PaymentRequest(this.address, this.amount, this.note, this.scheme, {this.callbackUrl, this.callbackMessage});
factory PaymentRequest.fromUri(Uri? uri) {
var address = "";
var amount = "";
var note = "";
var scheme = "";
String? callbackUrl;
String? callbackMessage;
if (uri != null) {
address = uri.path;
address = uri.queryParameters['address'] ?? uri.path;
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? "";
scheme = uri.scheme;
callbackUrl = uri.queryParameters['callback'];
callbackMessage = uri.queryParameters['callbackMessage'];
}
if (scheme == "nano-gpt") {
// treat as nano so filling out the address works:
scheme = "nano";
}
if (nano != null) {
@@ -26,11 +36,20 @@ class PaymentRequest {
}
}
return PaymentRequest(address, amount, note, scheme);
return PaymentRequest(
address,
amount,
note,
scheme,
callbackUrl: callbackUrl,
callbackMessage: callbackMessage,
);
}
final String address;
final String amount;
final String note;
final String scheme;
final String? callbackUrl;
final String? callbackMessage;
}
+118
View File
@@ -0,0 +1,118 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:mobx/mobx.dart';
part 'link_view_model.g.dart';
class LinkViewModel = LinkViewModelBase with _$LinkViewModel;
abstract class LinkViewModelBase with Store {
LinkViewModelBase({
required this.settingsStore,
required this.appStore,
required this.authenticationStore,
required this.navigatorKey,
}) {}
final SettingsStore settingsStore;
final AppStore appStore;
final AuthenticationStore authenticationStore;
final GlobalKey<NavigatorState> navigatorKey;
Uri? currentLink;
bool get _isValidPaymentUri => currentLink?.path.isNotEmpty ?? false;
bool get isWalletConnectLink => currentLink?.authority == 'wc';
bool get isNanoGptLink => currentLink?.scheme == 'nano-gpt';
String? getRouteToGo() {
if (isWalletConnectLink) {
if (!isEVMCompatibleChain(appStore.wallet!.type)) {
_errorToast(S.current.switchToEVMCompatibleWallet);
return null;
}
return Routes.walletConnectConnectionsListing;
}
if (authenticationStore.state == AuthenticationState.uninitialized) {
return null;
}
if (isNanoGptLink) {
switch (currentLink?.authority ?? '') {
case "exchange":
return Routes.exchange;
case "send":
return Routes.send;
case "buy":
return Routes.buySellPage;
}
}
if (_isValidPaymentUri) {
return Routes.send;
}
return null;
}
dynamic getRouteArgs() {
if (isWalletConnectLink) {
return currentLink;
}
if (isNanoGptLink) {
switch (currentLink?.authority ?? '') {
case "exchange":
case "send":
return PaymentRequest.fromUri(currentLink);
case "buy":
return true;
}
}
if (_isValidPaymentUri) {
return PaymentRequest.fromUri(currentLink);
}
return null;
}
Future<void> _errorToast(String message, {double fontSize = 16}) async {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.SNACKBAR,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: fontSize,
);
}
Future<void> handleLink() async {
String? route = getRouteToGo();
dynamic args = getRouteArgs();
if (route != null) {
if (appStore.wallet == null) {
return;
}
if (isNanoGptLink) {
if (route == Routes.buySellPage || route == Routes.exchange) {
await _errorToast(S.current.nano_gpt_thanks_message, fontSize: 14);
}
}
currentLink = null;
navigatorKey.currentState?.pushNamed(
route,
arguments: args,
);
}
}
}