dev
dart 181 lines 4.88 KB
Raw
1 import 'dart:async';
2
3 import 'package:cake_wallet/generated/i18n.dart';
4 import 'package:cake_wallet/reactions/wallet_connect.dart';
5 import 'package:cake_wallet/routes.dart';
6 import 'package:cake_wallet/store/app_store.dart';
7 import 'package:cake_wallet/store/authentication_store.dart';
8 import 'package:cake_wallet/store/settings_store.dart';
9 import 'package:cake_wallet/utils/payment_request.dart';
10 import 'package:flutter/material.dart';
11 import 'package:fluttertoast/fluttertoast.dart';
12
13 class LinkViewModel {
14 LinkViewModel({
15 required this.settingsStore,
16 required this.appStore,
17 required this.authenticationStore,
18 required this.navigatorKey,
19 });
20
21 final SettingsStore settingsStore;
22 final AppStore appStore;
23 final AuthenticationStore authenticationStore;
24 final GlobalKey<NavigatorState> navigatorKey;
25 Uri? currentLink;
26 bool readyToOpenPage = false;
27
28 final StreamController<Uri> _incomingPaymentLinksController = StreamController<Uri>.broadcast();
29
30 Stream<Uri> get incomingPaymentLinks => _incomingPaymentLinksController.stream;
31
32 bool get _isValidPaymentUri => currentLink?.path.isNotEmpty ?? false;
33 bool get isWalletConnectLink => currentLink?.authority == 'wc';
34 bool get isNanoGptLink => currentLink?.scheme == 'nano-gpt';
35 bool get isQuickActionLink =>
36 currentLink?.scheme == 'cakewallet' && currentLink?.host == 'quickaction';
37
38 String? getRouteToGo() {
39 if (isWalletConnectLink) {
40 if (!isWalletConnectCompatibleChain(appStore.wallet!.type)) {
41 _errorToast(S.current.switchToWCCompatibleWallet(walletConnectCompatibleChainsLabel()));
42 return null;
43 }
44 return Routes.walletConnectConnectionsListing;
45 }
46
47 // Check for a quick action first.
48 if (isQuickActionLink) {
49 final action = currentLink!.pathSegments.isNotEmpty ? currentLink!.pathSegments.first : null;
50 switch (action) {
51 case 'send':
52 return Routes.send;
53 case 'receive':
54 return Routes.newReceivePage;
55 default:
56 return null;
57 }
58 }
59
60 if (authenticationStore.state == AuthenticationState.uninitialized) {
61 return null;
62 }
63
64 if (isNanoGptLink) {
65 switch (currentLink?.authority ?? '') {
66 case "exchange":
67 return Routes.exchange;
68 case "send":
69 return Routes.send;
70 case "buy":
71 return Routes.buySellPage;
72 }
73 }
74
75 if (_isValidPaymentUri) {
76 return Routes.send;
77 }
78
79 return null;
80 }
81
82 dynamic getRouteArgs() {
83 if (isWalletConnectLink) {
84 return currentLink;
85 }
86
87 if (isQuickActionLink) {
88 return null;
89 }
90
91 if (isNanoGptLink) {
92 switch (currentLink?.authority ?? '') {
93 case "exchange":
94 return PaymentRequest.fromUri(currentLink);
95 case "send":
96 return {"paymentRequest": PaymentRequest.fromUri(currentLink)};
97 case "buy":
98 return true;
99 }
100 }
101
102 if (_isValidPaymentUri) {
103 return {
104 "paymentRequest": PaymentRequest.fromUri(currentLink),
105 "rawLink": currentLink.toString(),
106 };
107 }
108
109 return null;
110 }
111
112 Future<void> _errorToast(String message, {double fontSize = 16}) async {
113 try {
114 await Fluttertoast.showToast(
115 msg: message,
116 toastLength: Toast.LENGTH_LONG,
117 gravity: ToastGravity.SNACKBAR,
118 backgroundColor: Colors.black,
119 textColor: Colors.white,
120 fontSize: fontSize,
121 );
122 } catch (_) {}
123 }
124
125 Future<void> markReadyToOpenPage() async {
126 readyToOpenPage = true;
127 await handleLink();
128 }
129
130 Future<void> handleLink() async {
131 if (!readyToOpenPage) {
132 return;
133 }
134
135 String? route = getRouteToGo();
136 dynamic args = getRouteArgs();
137 if (route != null) {
138 if (appStore.wallet == null) {
139 return;
140 }
141
142 if (route == Routes.send &&
143 !isQuickActionLink &&
144 currentLink != null &&
145 _incomingPaymentLinksController.hasListener) {
146 final link = currentLink!;
147 currentLink = null;
148 _incomingPaymentLinksController.add(link);
149 return;
150 }
151
152 // Prevent navigating to the same route again and clear the link so a later auth
153 // event cannot refire it.
154 if (appStore.currentRouteName == route) {
155 currentLink = null;
156 return;
157 }
158
159 if (isNanoGptLink) {
160 if (route == Routes.buySellPage || route == Routes.exchange) {
161 await _errorToast(S.current.nano_gpt_thanks_message, fontSize: 14);
162 }
163 }
164
165 // Quick actions must reset navigation to Dashboard → TargetPage
166 if (isQuickActionLink) {
167 currentLink = null;
168 navigatorKey.currentState?.pushNamedAndRemoveUntil(
169 route,
170 ModalRoute.withName(Routes.dashboard),
171 arguments: args,
172 );
173 return;
174 }
175
176 // Normal navigation flow
177 currentLink = null;
178 navigatorKey.currentState?.pushNamed(route, arguments: args);
179 }
180 }
181 }