CW-1190: Minor Fixes (#2614)

* fix: Enhance WalletConnect URI handling and validation, better handle errors

* fix: Disable swipe to send if wallet is not synchronized

* fix: Handle session expiry gracefully

* fix: Disable swipe to send if wallet is not synced
This commit is contained in:
David Adegoke
2025-11-08 22:46:51 +01:00
committed by GitHub
parent ddf6043ed9
commit 9fa4584d97
9 changed files with 79 additions and 30 deletions
@@ -627,6 +627,7 @@ class CakePayBuyCardPage extends BasePage {
feeFiatAmount: _sendViewModel.pendingTransactionFeeFiatAmountFormatted,
outputs: displayingOutputs,
footerType: FooterType.slideActionButton,
isSlideActionEnabled: _sendViewModel.isReadyForSend,
slideActionButtonText:
cakePayBuyCardViewModel.isSimulating ? 'Swipe to simulate' : 'Swipe to send',
accessibleNavigationModeSlideActionButtonText:
@@ -284,6 +284,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
return ConfirmSendingBottomSheet(
key: ValueKey('exchange_trade_page_confirm_sending_bottom_sheet_key'),
footerType: FooterType.slideActionButton,
isSlideActionEnabled: widget.exchangeTradeViewModel.sendViewModel.isReadyForSend,
walletType: widget.exchangeTradeViewModel.sendViewModel.walletType,
titleText: S.of(bottomSheetContext).confirm_transaction,
titleIconPath:
+1
View File
@@ -567,6 +567,7 @@ class SendPage extends BasePage {
titleText: S.of(bottomSheetContext).confirm_transaction,
accessibleNavigationModeSlideActionButtonText: S.of(bottomSheetContext).send,
footerType: FooterType.slideActionButton,
isSlideActionEnabled: sendViewModel.isReadyForSend,
walletType: sendViewModel.walletType,
titleIconPath: sendViewModel.selectedCryptoCurrency.iconPath,
currency: sendViewModel.selectedCryptoCurrency,
@@ -190,6 +190,7 @@ class RBFDetailsPage extends BasePage {
return ConfirmSendingBottomSheet(
key: ValueKey('rbf_confirm_sending_bottom_sheet'),
titleText: S.of(bottomSheetContext).confirm_transaction,
isSlideActionEnabled: transactionDetailsViewModel.sendViewModel.isReadyForSend,
walletType: transactionDetailsViewModel.sendViewModel.walletType,
titleIconPath:
transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
@@ -202,6 +202,13 @@ abstract class WalletKitServiceBase with Store {
),
);
}
} on ReownSignError catch (e) {
if (e.code == 6) {
try {
await deletePairing(topic: session.pairingTopic);
} catch (_) {}
_refreshPairings();
}
} catch (_) {}
}
}
@@ -33,9 +33,13 @@ class WalletConnectConnectionsView extends StatelessWidget {
final query = actualLinkList[1];
final uri = Uri.decodeComponent(query);
final decoded = Uri.decodeComponent(query).trim();
final uriData = Uri.parse(uri);
final sanitized = decoded.startsWith('@') ? decoded.substring(1) : decoded;
final uriData = Uri.tryParse(sanitized);
if (uriData == null || (uriData.scheme.isEmpty)) return;
await walletKitService.pairWithUri(uriData);
}
@@ -88,7 +92,25 @@ class WCPairingsWidget extends BasePage {
if (walletConnectURI == null) return _invalidUriToast(context, S.current.nullURIError);
log('_onFoundUri: $walletConnectURI');
final Uri uriData = Uri.parse(walletConnectURI);
// Accept either a raw WC URI or a full URL containing `uri=` parameter
String input = walletConnectURI.trim();
if (input.contains('uri=')) {
final parts = input.split('uri=');
if (parts.length > 1) {
input = Uri.decodeComponent(parts.last);
}
}
// Some scanners may prefix with '@', strip it
if (input.startsWith('@')) {
input = input.substring(1);
}
final Uri? uriData = Uri.tryParse(input);
final bool hasValidScheme = uriData != null && uriData.scheme.isNotEmpty;
if (!hasValidScheme) {
return _invalidUriToast(context, S.current.invalid_input);
}
await walletKitService.pairWithUri(uriData);
}
@@ -121,10 +143,10 @@ class WCPairingsWidget extends BasePage {
Text(
S.current.connectWalletPrompt,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.normal,
color: Theme.of(context).colorScheme.onSurface,
),
fontSize: 16.0,
fontWeight: FontWeight.normal,
color: Theme.of(context).colorScheme.onSurface,
),
),
SizedBox(height: 16),
PrimaryButton(
@@ -156,10 +178,10 @@ class WCPairingsWidget extends BasePage {
S.current.activeConnectionsPrompt,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.normal,
color: Theme.of(context).colorScheme.onSurface,
),
fontSize: 16.0,
fontWeight: FontWeight.normal,
color: Theme.of(context).colorScheme.onSurface,
),
),
),
replacement: ListView.builder(
@@ -12,6 +12,7 @@ abstract class BaseBottomSheet extends StatelessWidget {
required this.footerType,
this.slideActionButtonText,
this.onSlideActionComplete,
this.isSlideActionEnabled = true,
this.singleActionButtonText,
this.accessibleNavigationModeSlideActionButtonText,
this.onSingleActionButtonPressed,
@@ -30,6 +31,7 @@ abstract class BaseBottomSheet extends StatelessWidget {
final FooterType footerType;
final String? slideActionButtonText;
final VoidCallback? onSlideActionComplete;
final bool isSlideActionEnabled;
final String? singleActionButtonText;
final String? accessibleNavigationModeSlideActionButtonText;
final VoidCallback? onSingleActionButtonPressed;
@@ -112,6 +114,7 @@ abstract class BaseBottomSheet extends StatelessWidget {
buttonText: slideActionButtonText ?? '',
onSlideComplete: onSlideActionComplete ?? () {},
accessibleNavigationModeButtonText: accessibleNavigationModeSlideActionButtonText ?? '',
isDisabled: !isSlideActionEnabled,
),
);
@@ -21,6 +21,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
String? titleIconPath,
String? slideActionButtonText,
VoidCallback? onSlideActionComplete,
bool isSlideActionEnabled = true,
String? accessibleNavigationModeSlideActionButtonText,
required this.currency,
this.paymentId,
@@ -48,6 +49,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
footerType: footerType,
slideActionButtonText: slideActionButtonText ?? 'Swipe to send',
onSlideActionComplete: onSlideActionComplete,
isSlideActionEnabled: isSlideActionEnabled,
accessibleNavigationModeSlideActionButtonText:
accessibleNavigationModeSlideActionButtonText,
key: key);
@@ -11,6 +11,7 @@ class StandardSlideButton extends StatefulWidget {
required this.accessibleNavigationModeButtonText,
this.tileBackgroundColor,
this.knobColor,
this.isDisabled = false,
}) : super(key: key);
final VoidCallback onSlideComplete;
@@ -19,6 +20,7 @@ class StandardSlideButton extends StatefulWidget {
final String accessibleNavigationModeButtonText;
final Color? tileBackgroundColor;
final Color? knobColor;
final bool isDisabled;
@override
StandardSlideButtonState createState() => StandardSlideButtonState();
@@ -36,14 +38,16 @@ class StandardSlideButtonState extends State<StandardSlideButton> {
Widget build(BuildContext context) {
final bool accessible = MediaQuery.of(context).accessibleNavigation;
final tileBackgroundColor = context.currentTheme.customColors.backgroundGradientColor;
final tileBackgroundColor = widget.isDisabled
? context.currentTheme.customColors.backgroundGradientColor.withOpacity(0.5)
: context.currentTheme.customColors.backgroundGradientColor;
return accessible
? PrimaryButton(
text: widget.accessibleNavigationModeButtonText,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
onPressed: () => widget.onSlideComplete(),
onPressed: widget.isDisabled ? null : () => widget.onSlideComplete(),
)
: LayoutBuilder(
builder: (context, constraints) {
@@ -56,7 +60,10 @@ class StandardSlideButtonState extends State<StandardSlideButton> {
height: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: widget.tileBackgroundColor ?? tileBackgroundColor,
color: (widget.isDisabled
? widget.tileBackgroundColor?.withOpacity(0.5)
: widget.tileBackgroundColor) ??
tileBackgroundColor,
),
child: Stack(
alignment: Alignment.centerLeft,
@@ -74,22 +81,26 @@ class StandardSlideButtonState extends State<StandardSlideButton> {
left: sideMargin + _dragPosition,
child: GestureDetector(
key: ValueKey('standard_slide_button_widget_slider_key'),
onHorizontalDragUpdate: (details) {
setState(() {
_dragPosition += details.delta.dx;
if (_dragPosition < 0) _dragPosition = 0;
if (_dragPosition > effectiveMaxWidth - sliderWidth) {
_dragPosition = effectiveMaxWidth - sliderWidth;
}
});
},
onHorizontalDragEnd: (details) {
if (_dragPosition >= effectiveMaxWidth - sliderWidth - 10) {
widget.onSlideComplete();
} else {
setState(() => _dragPosition = 0);
}
},
onHorizontalDragUpdate: widget.isDisabled
? null
: (details) {
setState(() {
_dragPosition += details.delta.dx;
if (_dragPosition < 0) _dragPosition = 0;
if (_dragPosition > effectiveMaxWidth - sliderWidth) {
_dragPosition = effectiveMaxWidth - sliderWidth;
}
});
},
onHorizontalDragEnd: widget.isDisabled
? null
: (details) {
if (_dragPosition >= effectiveMaxWidth - sliderWidth - 10) {
widget.onSlideComplete();
} else {
setState(() => _dragPosition = 0);
}
},
child: Container(
key: ValueKey('standard_slide_button_widget_slider_container_key'),
width: sliderWidth,