Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TW-1110 Change Wert popup UI and UX #1063

Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f8d4ef0
TW-1110 Change Wert popup UI and UX
keshan3262 Mar 15, 2024
197fb74
TW-1110 Refactor and change UX according to a new discussion
keshan3262 Mar 18, 2024
93a2b9b
TW-1110 Add forgotten changes
keshan3262 Mar 18, 2024
b9db33c
TW-1110 Refactoring according to comments
keshan3262 Mar 18, 2024
f7e62e5
Merge branch 'development' of https://github.com/madfish-solutions/te…
keshan3262 Mar 20, 2024
a85feec
TW-1110 Join resetting loader and onramp overlay into one action
keshan3262 Mar 20, 2024
4614784
TW-1110 Remove some hooks is RootStack
keshan3262 Mar 21, 2024
d95073b
TW-1110 Remove an unused Redux state variable
keshan3262 Mar 21, 2024
6958af8
Merge branch 'development' of https://github.com/madfish-solutions/te…
keshan3262 Mar 25, 2024
6bf1a8b
TW-1110 Refactoring according to comments
keshan3262 Mar 25, 2024
d5d3611
Merge branch 'development' of https://github.com/madfish-solutions/te…
keshan3262 Mar 27, 2024
89bc848
TW-1110 Refactoring according to comments
keshan3262 Mar 27, 2024
0fc4731
TW-1110 Fix ts error
keshan3262 Mar 27, 2024
3a26079
TW-1110 Remove unused Redux state fragment
keshan3262 Mar 27, 2024
ef14414
Merge branch 'development' of https://github.com/madfish-solutions/te…
keshan3262 Apr 3, 2024
4df98ad
TW-1110 Render bottom sheets for some time before opening
keshan3262 Apr 3, 2024
6230aba
TW-1110 Add memoization for WalletOverlay
keshan3262 Apr 3, 2024
ad16d68
TW-1110 Use 'index' property of GorhomBottomSheet
keshan3262 Apr 3, 2024
199237f
TW-1110 Remove an obsolete comment
keshan3262 Apr 3, 2024
78b617d
Merge branch 'development' of https://github.com/madfish-solutions/te…
keshan3262 Apr 4, 2024
c647029
TW-1110 Additional bugfixes
keshan3262 Apr 4, 2024
26f31b6
TW-1110 Implement suggestions from comments
keshan3262 Apr 5, 2024
3c9219c
Merge branch 'development' of https://github.com/madfish-solutions/te…
keshan3262 Apr 11, 2024
eed44c2
TW-1110 Fix effect error for iOS
keshan3262 Apr 12, 2024
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
45 changes: 33 additions & 12 deletions src/components/bottom-sheet/bottom-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,34 @@ import { BackHandler, Keyboard, Text, View } from 'react-native';
import { useOrientationChange } from 'react-native-orientation-locker';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import { emptyComponent, emptyFn, EmptyFn } from '../../config/general';
import { useAppLock } from '../../shelter/app-lock/app-lock';
import { formatSize } from '../../styles/format-size';
import { isDefined } from '../../utils/is-defined';
import { TouchableWithAnalytics } from 'src/components/touchable-with-analytics';
import { emptyComponent, emptyFn } from 'src/config/general';
import { useAppLock } from 'src/shelter/app-lock/app-lock';
import { formatSize } from 'src/styles/format-size';
import { isDefined } from 'src/utils/is-defined';

import { useDropdownBottomSheetStyles } from './bottom-sheet.styles';
import { BottomSheetControllerProps } from './use-bottom-sheet-controller';

interface Props extends BottomSheetControllerProps {
title?: string;
description: string;
description?: string;
cancelButtonText?: string;
onCancelButtonPress?: EmptyFn;
cancelButtonTestID?: string;
onClose?: EmptyFn;
contentHeight: number;
isInitiallyOpen?: boolean;
}

export const BottomSheet: FC<Props> = ({
title,
description,
cancelButtonText = 'Cancel',
cancelButtonTestID,
isInitiallyOpen = false,
onCancelButtonPress = emptyFn,
onClose = emptyFn,
contentHeight,
controller,
children
Expand Down Expand Up @@ -65,6 +72,12 @@ export const BottomSheet: FC<Props> = ({
controller.close();
onCancelButtonPress();
};
const handleClose = () => {
if (isOpened) {
setIsOpened(false);
onClose();
}
};

useEffect(() => {
if (isOpened) {
Expand All @@ -85,26 +98,34 @@ export const BottomSheet: FC<Props> = ({
{!isLocked && (
<GorhomBottomSheet
ref={controller.ref}
index={-1}
index={isInitiallyOpen ? 0 : -1}
snapPoints={[contentHeight]}
enablePanDownToClose={true}
bottomInset={bottomInset}
handleComponent={emptyComponent}
backgroundComponent={emptyComponent}
backdropComponent={renderBackdropComponent}
onChange={handleChange}
onClose={handleClose}
>
<View style={styles.root}>
<View style={styles.headerContainer}>
{isDefined(title) && <Text style={styles.title}>{title}</Text>}
<Text style={styles.description}>{description}</Text>
</View>
{(isDefined(title) || isDefined(description)) && (
<View style={styles.headerContainer}>
{isDefined(title) && <Text style={styles.title}>{title}</Text>}
{isDefined(description) && <Text style={styles.description}>{description}</Text>}
</View>
)}

{children}

<TouchableOpacity style={styles.cancelButton} onPress={handleCancelPress}>
<TouchableWithAnalytics
Component={TouchableOpacity}
testID={cancelButtonTestID}
style={styles.cancelButton}
onPress={handleCancelPress}
>
<Text style={styles.cancelButtonText}>{cancelButtonText}</Text>
</TouchableOpacity>
</TouchableWithAnalytics>
</View>
</GorhomBottomSheet>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Animated } from 'react-native';
import { useDispatch } from 'react-redux';

import { isAndroid, isIOS } from 'src/config/system';
import { OnRampOverlayState } from 'src/enums/on-ramp-overlay-state.enum';
import { useCanUseOnRamp } from 'src/hooks/use-can-use-on-ramp.hook';
import { useAtBootsplash } from 'src/hooks/use-hide-bootsplash';
import { useNetworkInfo } from 'src/hooks/use-network-info.hook';
import { useTotalBalance } from 'src/hooks/use-total-balance';
Expand All @@ -12,7 +14,7 @@ import { ScreensEnum } from 'src/navigator/enums/screens.enum';
import { useNavigation } from 'src/navigator/hooks/use-navigation.hook';
import { WalletSelectors } from 'src/screens/wallet/wallet.selectors';
import { useAppLock } from 'src/shelter/app-lock/app-lock';
import { setOnRampPossibilityAction } from 'src/store/settings/settings-actions';
import { setOnRampOverlayStateAction } from 'src/store/settings/settings-actions';
import { useIsShowLoaderSelector } from 'src/store/settings/settings-selectors';
import { formatSize } from 'src/styles/format-size';
import { showErrorToast } from 'src/toast/toast.utils';
Expand Down Expand Up @@ -40,6 +42,7 @@ export const HeaderCardActionButtons: FC<Props> = ({ token }) => {
const { navigate } = useNavigation();
const { isLocked } = useAppLock();
const atBootsplash = useAtBootsplash();
const canUseOnRamp = useCanUseOnRamp();
const { metadata, isTezosNode, isTezosMainnet } = useNetworkInfo();
const tezosToken = useTezosTokenOfCurrentAccount();
const { balance } = useTotalBalance();
Expand Down Expand Up @@ -112,7 +115,7 @@ export const HeaderCardActionButtons: FC<Props> = ({ token }) => {
}

showErrorToast({ description: errorMessage });
dispatch(setOnRampPossibilityAction(true));
canUseOnRamp && dispatch(setOnRampOverlayStateAction(OnRampOverlayState.Continue));
};

return (
Expand Down
5 changes: 5 additions & 0 deletions src/enums/on-ramp-overlay-state.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum OnRampOverlayState {
Start = 'start',
Continue = 'continue',
Closed = 'closed'
}
4 changes: 2 additions & 2 deletions src/form/validation/asset-amount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const createAssetAmountWithMaxValidation = (
assetAmountValidation.clone().test('max-amount', (value, context) => {
const { asset, amount } = value;

if (!isDefined(asset?.balance) || !isDefined(amount)) {
if (!isDefined(asset?.balance) || !(amount instanceof BigNumber)) {
return true;
}

Expand All @@ -42,7 +42,7 @@ export const createAssetAmountWithMaxValidation = (
const maxAmount = BigNumber.max(new BigNumber(asset.balance).minus(gasAmountCap), 0);
const displayedMaxAmount = formatAssetAmount(mutezToTz(maxAmount, asset.decimals ?? 0));

return (amount as BigNumber).lte(maxAmount)
return amount.lte(maxAmount)
? true
: new ValidationError(
`Maximal amount is ${displayedMaxAmount} ${asset.symbol}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ParamsWithKind } from '@taquito/taquito';

import { AccountInterface } from '../../interfaces/account.interface';
import { ReadOnlySignerPayload } from 'src/types/read-only-signer-payload';

export interface ApproveInternalOperationRequestActionPayloadInterface {
rpcUrl: string;
sender: AccountInterface;
sender: ReadOnlySignerPayload;
opParams: ParamsWithKind[];
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { OperationRequestOutput } from '@airgap/beacon-sdk';
import { ParamsWithKind } from '@taquito/taquito';

import { AccountInterface } from '../../interfaces/account.interface';
import { ReadOnlySignerPayload } from 'src/types/read-only-signer-payload';

export interface ApproveOperationRequestActionPayloadInterface {
rpcUrl: string;
sender: AccountInterface;
sender: ReadOnlySignerPayload;
opParams: ParamsWithKind[];
message: OperationRequestOutput;
}
4 changes: 2 additions & 2 deletions src/hooks/root-hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useFirebaseApp } from './use-firebase-app';
import { usePushNotifications } from './use-push-notifications';
import { useQuickActions } from './use-quick-actions';
import { useResetKeychainOnInstall } from './use-reset-keychain-on-install';
import { useResetLoading } from './use-reset-loading';
import { useResetPermanentInitialSettings } from './use-reset-permanent-initial-settings';
import { useScamlistLoading } from './use-scamlist-loading';
import { useStorageAnalytics } from './use-storage-analytics';
import { useWhitelistLoading } from './use-whitelist-loading';
Expand All @@ -15,7 +15,7 @@ export const useRootHooks = () => {
useScamlistLoading();
useCollectiblesDetailsLoading();
useQuickActions();
useResetLoading();
useResetPermanentInitialSettings();
useResetKeychainOnInstall();

useFirebaseApp();
Expand Down
10 changes: 0 additions & 10 deletions src/hooks/root-hooks/use-reset-loading.ts

This file was deleted.

11 changes: 11 additions & 0 deletions src/hooks/root-hooks/use-reset-permanent-initial-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';

import { resetPermanentInitialSettingsAction } from 'src/store/settings/settings-actions';

export const useResetPermanentInitialSettings = () => {
const dispatch = useDispatch();

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => void dispatch(resetPermanentInitialSettingsAction()), []);
};
9 changes: 9 additions & 0 deletions src/hooks/use-can-use-on-ramp.hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { isAndroid } from 'src/config/system';

import { useNetworkInfo } from './use-network-info.hook';

export const useCanUseOnRamp = () => {
const { isTezosNode } = useNetworkInfo();

return isTezosNode && isAndroid;
};
16 changes: 16 additions & 0 deletions src/hooks/use-on-ramp-continue-overlay.hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';

import { OnRampOverlayState } from 'src/enums/on-ramp-overlay-state.enum';
import { setOnRampOverlayStateAction } from 'src/store/settings/settings-actions';
import { useOnRampOverlayStateSelector } from 'src/store/settings/settings-selectors';

export const useOnRampContinueOverlay = () => {
const dispatch = useDispatch();
const state = useOnRampOverlayStateSelector();
const isOpened = state === OnRampOverlayState.Continue;

const onClose = useCallback(() => dispatch(setOnRampOverlayStateAction(OnRampOverlayState.Closed)), [dispatch]);

return { isOpened, onClose };
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const approveOperationRequest = ({
opParams,
message
}: ApproveOperationRequestActionPayloadInterface) =>
sendTransaction$(rpcUrl, sender, opParams).pipe(
sendTransaction$(rpcUrl, sender.publicKeyHash, opParams).pipe(
switchMap(({ hash }) =>
from(
BeaconHandler.respond({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { OpKind } from '@taquito/taquito';
import { BigNumber } from 'bignumber.js';
import React, { FC, useEffect, useMemo } from 'react';
import React, { FC, useCallback, useRef } from 'react';
import { useDispatch } from 'react-redux';
import { of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
Expand All @@ -9,15 +9,16 @@ import { everstakeApi } from 'src/api.service';
import { Disclaimer } from 'src/components/disclaimer/disclaimer';
import { HeaderTitle } from 'src/components/header/header-title/header-title';
import { useNavigationSetOptions } from 'src/components/header/use-navigation-set-options.hook';
import { OnRampOverlayState } from 'src/enums/on-ramp-overlay-state.enum';
import { ApproveInternalOperationRequestActionPayloadInterface } from 'src/hooks/request-confirmation/approve-internal-operation-request-action-payload.interface';
import { useRequestConfirmation } from 'src/hooks/request-confirmation/use-request-confirmation.hook';
import { useCanUseOnRamp } from 'src/hooks/use-can-use-on-ramp.hook';
import { StacksEnum } from 'src/navigator/enums/stacks.enum';
import { OnRampOverlay } from 'src/screens/wallet/on-ramp-overlay/on-ramp-overlay';
import { navigateAction } from 'src/store/root-state.actions';
import { setOnRampPossibilityAction } from 'src/store/settings/settings-actions';
import { setOnRampOverlayStateAction } from 'src/store/settings/settings-actions';
import { useSelectedRpcUrlSelector } from 'src/store/settings/settings-selectors';
import { waitForOperationCompletionAction } from 'src/store/wallet/wallet-actions';
import { useCurrentAccountTezosBalance, useSelectedAccountSelector } from 'src/store/wallet/wallet-selectors';
import { useCurrentAccountTezosBalance, useRawCurrentAccountSelector } from 'src/store/wallet/wallet-selectors';
import { showSuccessToast } from 'src/toast/toast.utils';
import { TEMPLE_WALLET_EVERSTAKE_LINK_ID } from 'src/utils/env.utils';
import { isDefined } from 'src/utils/is-defined';
Expand All @@ -28,14 +29,16 @@ import { sendTransaction$ } from 'src/utils/wallet.utils';
import { InternalOperationsConfirmationModalParams } from '../confirmation-modal.params';
import { OperationsConfirmation } from '../operations-confirmation/operations-confirmation';

const NOT_ENOUGH_TEZ_ERRORS_KEYWORDS = ['empty_implicit_contract', 'empty_implicit_delegated_contract'];

type Props = Omit<InternalOperationsConfirmationModalParams, 'type'>;

const approveInternalOperationRequest = ({
rpcUrl,
sender,
opParams
}: ApproveInternalOperationRequestActionPayloadInterface) =>
sendTransaction$(rpcUrl, sender, opParams).pipe(
sendTransaction$(rpcUrl, sender.publicKeyHash, opParams).pipe(
switchMap(({ hash }) =>
opParams[0]?.kind === OpKind.DELEGATION && opParams[0]?.delegate === RECOMMENDED_BAKER_ADDRESS
? of(
Expand All @@ -58,28 +61,15 @@ const approveInternalOperationRequest = ({
);

export const InternalOperationsConfirmation: FC<Props> = ({ opParams, modalTitle, disclaimerMessage, testID }) => {
const canUseOnRamp = useCanUseOnRamp();
const dispatch = useDispatch();
const selectedAccount = useSelectedAccountSelector();
const selectedAccount = useRawCurrentAccountSelector();
const rpcUrl = useSelectedRpcUrlSelector();
const tezBalance = useCurrentAccountTezosBalance();
const tezosBalance = useCurrentAccountTezosBalance();
const lastSetOverlayStateRef = useRef<OnRampOverlayState | null>(null);

const { confirmRequest, isLoading } = useRequestConfirmation(approveInternalOperationRequest);

const totalTransactionCost = useMemo(() => {
if (opParams[0]?.kind === OpKind.TRANSACTION) {
// @ts-ignore
return BigNumber.sum(...opParams.map(({ amount }) => amount));
}

return new BigNumber(0);
}, [opParams]);

useEffect(() => {
if (new BigNumber(tezBalance).isLessThanOrEqualTo(totalTransactionCost)) {
dispatch(setOnRampPossibilityAction(true));
}
}, [tezBalance, totalTransactionCost]);

useNavigationSetOptions(
{
headerTitle: () => {
Expand All @@ -100,17 +90,45 @@ export const InternalOperationsConfirmation: FC<Props> = ({ opParams, modalTitle
<Disclaimer title="Disclaimer" texts={[disclaimerMessage]} />
) : undefined;

const updateOverlayState = useCallback(
(newState: OnRampOverlayState) => {
if (lastSetOverlayStateRef.current !== newState) {
lastSetOverlayStateRef.current = newState;
dispatch(setOnRampOverlayStateAction(newState));
}
},
[dispatch]
);

const handleEstimationError = useCallback(
(error: string) =>
NOT_ENOUGH_TEZ_ERRORS_KEYWORDS.some(keyword => error.includes(keyword)) && canUseOnRamp
? updateOverlayState(OnRampOverlayState.Continue)
: console.error(error),
[canUseOnRamp, updateOverlayState]
);

const handleTotalTezValue = useCallback(
(newValue: BigNumber) =>
canUseOnRamp &&
updateOverlayState(newValue.gt(tezosBalance) ? OnRampOverlayState.Continue : OnRampOverlayState.Closed),
[canUseOnRamp, tezosBalance, updateOverlayState]
);

return (
<>
<OperationsConfirmation
sender={selectedAccount}
opParams={opParams}
isLoading={isLoading}
onSubmit={newOpParams => confirmRequest({ rpcUrl, sender: selectedAccount, opParams: newOpParams })}
testID={testID}
disclaimer={disclaimer}
/>
<OnRampOverlay />
{isDefined(selectedAccount) && (
<OperationsConfirmation
sender={selectedAccount}
opParams={opParams}
isLoading={isLoading}
onEstimationError={handleEstimationError}
onTotalTezValue={handleTotalTezValue}
onSubmit={newOpParams => confirmRequest({ rpcUrl, sender: selectedAccount, opParams: newOpParams })}
testID={testID}
disclaimer={disclaimer}
/>
)}
</>
);
};
Loading
Loading