Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions assets/web_pages/fiat_widget.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<body>
<iframe id="fiat-onramp-iframe" title="Fiat On-Ramp Widget"
sandbox="allow-forms allow-scripts allow-same-origin allow-popups allow-top-navigation allow-top-navigation-by-user-action"
src="">
allow="payment; encrypted-media; microphone; camera; midi" src="">
<!-- Placeholder fallback message -->
<p>Your browser does not support iframes.</p>
<p>Please use a modern browser to view this content.</p>
Expand Down Expand Up @@ -67,7 +67,7 @@

/**
* Get URL parameter by name
*
*
* @param {string} name - The name of the URL parameter to retrieve
* @returns {string|null} - The value of the URL parameter or null if not found
*/
Expand All @@ -76,10 +76,10 @@
return params.get(name);
}

/**
/**
* Handle messages from the iframe
*
* @param {MessageEvent} messageEvent
*
* @param {MessageEvent} messageEvent
*/
function _komodoOnMessageHandler(messageEvent) {
let messageData;
Expand All @@ -96,18 +96,18 @@
}
}

/**
/**
* Post a message to the parent window
*
* @param {string|object} messageData
*/
*
* @param {string|object} messageData
*/
function _komodoPostMessageToParent(messageData) {
const messageString = (typeof messageData === 'object') ? JSON.stringify(messageData) : String(messageData);

// flutter_inappwebview
console.log(messageString);

// universal_url opener
// universal_url opener
if (window.opener) {
return window.opener.postMessage(messageString, "*");
}
Expand All @@ -117,7 +117,7 @@
}

// Windows WebView2 (desktop_webview_window)
// https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/communicate-btwn-web-native
// https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/communicate-btwn-web-native
if (window.chrome && window.chrome.webview) {
return window.chrome.webview.postMessage(messageString);
}
Expand Down
24 changes: 16 additions & 8 deletions lib/bloc/fiat/fiat_onramp_form/fiat_form_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class FiatFormBloc extends Bloc<FiatFormEvent, FiatFormState> {
// can happen frequently and we want to avoid race conditions.
on<FiatFormCoinSelected>(_onCoinSelected, transformer: restartable());
on<FiatFormPaymentMethodSelected>(_onPaymentMethodSelected);
on<FiatFormSubmitted>(_onFormSubmitted);
// Use droppable here to prevent multiple simultaneous submissions
on<FiatFormSubmitted>(_onFormSubmitted, transformer: droppable());
on<FiatFormPaymentStatusMessageReceived>(_onPaymentStatusMessage);
on<FiatFormModeUpdated>(_onModeUpdated);
on<FiatFormResetRequested>(_onAccountCleared);
Expand Down Expand Up @@ -169,15 +170,18 @@ class FiatFormBloc extends Bloc<FiatFormEvent, FiatFormState> {
FiatFormSubmitted event,
Emitter<FiatFormState> emit,
) async {
final formValidationError = getFormIssue();
final formValidationError = _getFormIssue();
if (formValidationError != null || !state.isValid) {
_log.warning('Form validation failed. Validation: ${state.isValid}');
return;
}

if (state.checkoutUrl.isNotEmpty) {
emit(state.copyWith(checkoutUrl: ''));
}
emit(
state.copyWith(
fiatOrderStatus: FiatOrderStatus.submitting,
checkoutUrl: '',
),
);

try {
final newOrder = await _fiatRepository.buyCoin(
Expand Down Expand Up @@ -229,12 +233,11 @@ class FiatFormBloc extends Bloc<FiatFormEvent, FiatFormState> {
WebViewDialogMode _determineWebViewMode() {
final bool isLinux = !kIsWeb && !kIsWasm && Platform.isLinux;
const bool isWeb = kIsWeb || kIsWasm;
final bool isBanxa = state.selectedPaymentMethod.providerId == 'Banxa';

// Banxa "Return to Komodo" button attempts to navigate the top window to
// the return URL, which is not supported in a dialog. So we need to open
// it in a new tab.
if (isLinux || (isWeb && isBanxa)) {
if (isLinux || (isWeb && state.isBanxaSelected)) {
return WebViewDialogMode.newTab;
} else if (isWeb) {
return WebViewDialogMode.dialog;
Expand Down Expand Up @@ -415,7 +418,12 @@ class FiatFormBloc extends Bloc<FiatFormEvent, FiatFormState> {
);
}

String? getFormIssue() {
@Deprecated(
'Validation is handled by formz in dedicated inputs like [FiatAmountInput]'
'This function should be removed once the cases are confirmed to be '
'covered by formz inputs',
)
String? _getFormIssue() {
// TODO: ? show on the UI and localise? These are currently used as more of
// a boolean "is there an error?" rather than "what is the error?"
if (state.paymentMethods.isEmpty) {
Expand Down
3 changes: 3 additions & 0 deletions lib/bloc/fiat/fiat_onramp_form/fiat_form_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ final class FiatFormState extends Equatable with FormzMixin {
!fiatOrderStatus.isSubmitting &&
isValid;

/// Whether the selected payment method is from the Banxa provider
bool get isBanxaSelected => selectedPaymentMethod.providerId == 'Banxa';

FiatFormState copyWith({
CurrencyInput<FiatCurrency>? selectedFiat,
CurrencyInput<CryptoCurrency>? selectedAsset,
Expand Down
17 changes: 13 additions & 4 deletions lib/bloc/fiat/fiat_order_status.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ enum FiatOrderStatus {
/// Initial status: User has not yet started the payment process
initial,

/// User has started the process, and the payment method has been opened.
/// E.g. Ramp or Banxa websites have been opened
/// Form is being submitted: Order is being created with the provider.
/// This is the transitional state while waiting for the provider API
/// response with the checkout URL.
submitting,

/// Order successfully created and checkout URL received from provider.
/// The webview dialog is ready to be opened/is opening with the provider's
/// payment page (e.g., Ramp or Banxa). This state triggers the webview
/// display and starts the order status monitoring.
submitted,

/// Payment is awaiting user action (e.g., user needs to complete payment)
Expand All @@ -30,6 +37,7 @@ enum FiatOrderStatus {
bool get isSubmitting =>
this == FiatOrderStatus.inProgress ||
this == FiatOrderStatus.submitted ||
this == FiatOrderStatus.submitting ||
this == FiatOrderStatus.pendingPayment;
bool get isFailed => this == FiatOrderStatus.failed;
bool get isSuccess => this == FiatOrderStatus.success;
Expand Down Expand Up @@ -66,8 +74,9 @@ enum FiatOrderStatus {
// to avoid alarming users with "Payment failed" popup messages
// unless we are sure that the payment has failed.
// Ideally, this section should not be reached.
Logger('FiatOrderStatus')
.warning('Unknown status: $status, defaulting to in progress');
Logger(
'FiatOrderStatus',
).warning('Unknown status: $status, defaulting to in progress');
return FiatOrderStatus.inProgress;
}
}
Expand Down
98 changes: 54 additions & 44 deletions lib/bloc/taker_form/taker_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import 'dart:async';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:logging/logging.dart';
import 'package:rational/rational.dart';
import 'package:web_dex/analytics/events/transaction_events.dart';
import 'package:web_dex/app_config/app_config.dart';
import 'package:web_dex/bloc/analytics/analytics_bloc.dart';
import 'package:web_dex/bloc/coins_bloc/coins_repo.dart';
import 'package:web_dex/bloc/dex_repository.dart';
import 'package:web_dex/bloc/taker_form/taker_event.dart';
import 'package:web_dex/bloc/taker_form/taker_state.dart';
import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
import 'package:web_dex/bloc/taker_form/taker_validator.dart';
import 'package:web_dex/bloc/transformers.dart';
import 'package:web_dex/mm2/mm2_api/rpc/base.dart';
Expand All @@ -24,11 +27,9 @@ import 'package:web_dex/model/data_from_service.dart';
import 'package:web_dex/model/dex_form_error.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/model/trade_preimage.dart';
import 'package:web_dex/model/wallet.dart';
import 'package:web_dex/shared/utils/utils.dart';
import 'package:web_dex/views/dex/dex_helpers.dart';
import 'package:web_dex/bloc/analytics/analytics_bloc.dart';
import 'package:web_dex/analytics/events/transaction_events.dart';
import 'package:web_dex/model/wallet.dart';

class TakerBloc extends Bloc<TakerEvent, TakerState> {
TakerBloc({
Expand Down Expand Up @@ -99,6 +100,7 @@ class TakerBloc extends Bloc<TakerEvent, TakerState> {
bool _isLoggedIn = false;
late TakerValidator _validator;
late StreamSubscription<KdfUser?> _authorizationSubscription;
final Logger _log = Logger('TakerBloc');

Future<void> _onStartSwap(
TakerStartSwap event,
Expand All @@ -123,8 +125,7 @@ class TakerBloc extends Bloc<TakerEvent, TakerState> {

// Log swap failure analytics event for immediate RPC errors
final walletType =
(await _sdk.auth.currentUser)?.wallet.config.type.name ??
'unknown';
(await _sdk.auth.currentUser)?.wallet.config.type.name ?? 'unknown';
_analyticsBloc.logEvent(
SwapFailedEventData(
asset: state.sellCoin!.abbr,
Expand Down Expand Up @@ -447,53 +448,62 @@ class TakerBloc extends Bloc<TakerEvent, TakerState> {
);
}

// Required here because of the manual RPC calls that bypass the sdk
final activeAssets = await _sdk.assets.getActivatedAssets();
final isAssetActive = activeAssets.any(
(asset) => asset.id == state.sellCoin!.id,
);
if (!isAssetActive) {
// Intentionally leave the state as loading so that a spinner is shown
// instead of a "0.00" balance hinting that the asset is active when it
// is not.
if (state.availableBalanceState != AvailableBalanceState.loading) {
emitter(
state.copyWith(
availableBalanceState: () => AvailableBalanceState.loading,
),
);
try {
// Required here because of the manual RPC calls that bypass the sdk
final activeAssets = await _sdk.assets.getActivatedAssets();
final isAssetActive = activeAssets.any(
(asset) => asset.id == state.sellCoin!.id,
);
if (!isAssetActive) {
// Intentionally leave the state as loading so that a spinner is shown
// instead of a "0.00" balance hinting that the asset is active when it
// is not.
if (state.availableBalanceState != AvailableBalanceState.loading) {
emitter(
state.copyWith(
availableBalanceState: () => AvailableBalanceState.loading,
),
);
}
return;
}
return;
}

if (!_isLoggedIn) {
emitter(
state.copyWith(
availableBalanceState: () => AvailableBalanceState.unavailable,
),
);
} else {
Rational? maxSellAmount = await _dexRepo.getMaxTakerVolume(
state.sellCoin!.abbr,
);
if (maxSellAmount != null) {
if (!_isLoggedIn) {
emitter(
state.copyWith(
maxSellAmount: () => maxSellAmount,
availableBalanceState: () => AvailableBalanceState.success,
availableBalanceState: () => AvailableBalanceState.unavailable,
),
);
} else {
maxSellAmount = await _frequentlyGetMaxTakerVolume();
emitter(
state.copyWith(
maxSellAmount: () => maxSellAmount,
availableBalanceState: maxSellAmount == null
? () => AvailableBalanceState.failure
: () => AvailableBalanceState.success,
),
Rational? maxSellAmount = await _dexRepo.getMaxTakerVolume(
state.sellCoin!.abbr,
);
if (maxSellAmount != null) {
emitter(
state.copyWith(
maxSellAmount: () => maxSellAmount,
availableBalanceState: () => AvailableBalanceState.success,
),
);
} else {
maxSellAmount = await _frequentlyGetMaxTakerVolume();
emitter(
state.copyWith(
maxSellAmount: () => maxSellAmount,
availableBalanceState: maxSellAmount == null
? () => AvailableBalanceState.failure
: () => AvailableBalanceState.success,
),
);
}
}
} catch (e, s) {
_log.severe('Failed to update max sell amount', e, s);
emitter(
state.copyWith(
availableBalanceState: () => AvailableBalanceState.failure,
),
);
}
}

Expand Down
16 changes: 4 additions & 12 deletions lib/views/common/pages/page_layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,15 @@ class _MobileLayout extends StatelessWidget {
return Column(
mainAxisSize: MainAxisSize.max,
children: [
const BackupSeedNotification(),
const BackupSeedNotification(hideOnMobile: false),
if (header != null) header!,
Flexible(
child: PagePlate(
noBackground: noBackground,
padding: padding,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
content,
],
),
child: Column(mainAxisSize: MainAxisSize.max, children: [content]),
Comment thread
takenagain marked this conversation as resolved.
),
)
),
],
);
}
Expand Down Expand Up @@ -97,10 +92,7 @@ class _DesktopLayout extends StatelessWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (header != null) ...[
const SizedBox(height: 23),
header!,
],
if (header != null) ...[const SizedBox(height: 23), header!],
content,
],
),
Expand Down
Loading
Loading