Skip to content

Commit

Permalink
feat(ramp): add sell notification texts (#7962)
Browse files Browse the repository at this point in the history
## **Description**

This PR adds the cases for sell orders notifications when the order is
added or changes states.


## **Related issues**

Fixes: #

## **Manual testing steps**

Perform a sale order and confirm the notifications text are matching

## **Screenshots/Recordings**

### **Before**

<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/953272eb-604b-4910-a73f-0bdb14355e1d"
/>
<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/ab6e1af0-a8f9-4856-8d7f-fac596129471"
/>
<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/d684ae58-d9ab-47be-b965-02ea7d90a9f6"
/>
<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/14640652-f11a-45e5-9f36-532b1dda8fc8"
/>

### **After**


<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/de48b4f4-d942-4227-9a65-1c677249c841"
/>
<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/fc336ded-f198-4f50-9bb0-aac544c7fc8c"
/>
<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/7c782c71-e8a9-4718-8e45-cec69d80be91"
/>
<img width="175"
src="https://github.com/MetaMask/metamask-mobile/assets/1024246/30b62b93-96c0-44e2-89de-5fb307fc250a"
/>


## **Pre-merge author checklist**

- [x] I’ve followed [MetaMask Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've clearly explained what problem this PR is solving and how it
is solved.
- [x] I've linked related issues
- [x] I've included manual testing steps
- [x] I've included screenshots/recordings if applicable
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
- [x] I’ve properly set the pull request status:
  - [x] In case it's not yet "ready for review", I've set it to "draft".
- [x] In case it's "ready for review", I've changed it from "draft" to
"non-draft".

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
  • Loading branch information
wachunei authored Dec 8, 2023
1 parent b77d86f commit 444b319
Show file tree
Hide file tree
Showing 28 changed files with 499 additions and 289 deletions.
6 changes: 3 additions & 3 deletions app/components/UI/Ramp/buy/hooks/useHandleSuccessfulOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { OrderOrderTypeEnum } from '@consensys/on-ramp-sdk/dist/API';
import { useNavigation } from '@react-navigation/native';
import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getNotificationDetails } from '../..';

import { protectWalletModalVisible } from '../../../../../actions/user';
import { NATIVE_ADDRESS } from '../../../../../constants/on-ramp';
import Engine from '../../../../../core/Engine';
Expand All @@ -12,7 +12,7 @@ import { addFiatOrder, FiatOrder } from '../../../../../reducers/fiatOrders';
import { toLowerCaseEquals } from '../../../../../util/general';
import useThunkDispatch from '../../../../hooks/useThunkDispatch';
import { useRampSDK } from '../../common/sdk';
import { stateHasOrder } from '../../common/utils';
import { getNotificationDetails, stateHasOrder } from '../../common/utils';
import useAnalytics from '../../common/hooks/useAnalytics';
import { hexToBN } from '../../../../../util/number';
import { selectAccounts } from '../../../../../selectors/accountTrackerController';
Expand Down Expand Up @@ -82,7 +82,7 @@ function useHandleSuccessfulOrder() {
return;
}
handleAddFiatOrder(order);
const notificationDetails = getNotificationDetails(order as any);
const notificationDetails = getNotificationDetails(order);
if (notificationDetails) {
NotificationManager.showSimpleNotification(notificationDetails);
}
Expand Down
9 changes: 6 additions & 3 deletions app/components/UI/Ramp/common/containers/ApplePayButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,12 @@ const ApplePayButton = ({
} catch (error: any) {
NotificationManager.showSimpleNotification({
duration: 5000,
title: strings('fiat_on_ramp.notifications.purchase_failed_title', {
currency: quote.crypto?.symbol,
}),
title: strings(
'fiat_on_ramp_aggregator.notifications.purchase_failed_title',
{
currency: quote.crypto?.symbol,
},
),
description: error.message,
status: 'error',
});
Expand Down
119 changes: 119 additions & 0 deletions app/components/UI/Ramp/common/utils/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import {
isSellQuote,
isSellOrder,
isSellFiatOrder,
getNotificationDetails,
} from '.';
import { FIAT_ORDER_STATES } from '../../../../../constants/on-ramp';
import { FiatOrder, RampType } from '../../../../../reducers/fiatOrders/types';

describe('timeToDescription', () => {
Expand Down Expand Up @@ -333,3 +335,120 @@ describe('Type assertion functions', () => {
});
});
});

describe('getNotificationDetails', () => {
const mockOrder = {
state: FIAT_ORDER_STATES.PENDING,
orderType: OrderOrderTypeEnum.Buy,
cryptocurrency: 'ETH',
cryptoAmount: '0.01',
} as FiatOrder;

const mockSellOrder = {
...mockOrder,
orderType: OrderOrderTypeEnum.Sell,
};

it('should return correct details for buy orders', () => {
const pendingDetails = getNotificationDetails(mockOrder);
expect(pendingDetails).toMatchInlineSnapshot(`
Object {
"description": "This should only take a few minutes...",
"duration": 5000,
"status": "pending",
"title": "Processing your purchase of ETH",
}
`);
const cancelledDetails = getNotificationDetails({
...mockOrder,
state: FIAT_ORDER_STATES.CANCELLED,
});
expect(cancelledDetails).toMatchInlineSnapshot(`
Object {
"description": "Verify your payment method and card support",
"duration": 5000,
"status": "cancelled",
"title": "Your purchase was cancelled",
}
`);
const failedDetails = getNotificationDetails({
...mockOrder,
state: FIAT_ORDER_STATES.FAILED,
});
expect(failedDetails).toMatchInlineSnapshot(`
Object {
"description": "Verify your payment method and card support",
"duration": 5000,
"status": "error",
"title": "Purchase of ETH has failed! Please try again, sorry for the inconvenience!",
}
`);

const completedDetails = getNotificationDetails({
...mockOrder,
state: FIAT_ORDER_STATES.COMPLETED,
});
expect(completedDetails).toMatchInlineSnapshot(`
Object {
"description": "Your ETH is now available",
"duration": 5000,
"status": "success",
"title": "Your purchase of 0.01 ETH was successful!",
}
`);
});

it('should return correct details for sell orders', () => {
const pendingDetails = getNotificationDetails(mockSellOrder);
expect(pendingDetails).toMatchInlineSnapshot(`
Object {
"description": "Your order is now being processed.",
"duration": 5000,
"status": "pending",
"title": "ETH sale processing",
}
`);
const cancelledDetails = getNotificationDetails({
...mockSellOrder,
state: FIAT_ORDER_STATES.CANCELLED,
});
expect(cancelledDetails).toMatchInlineSnapshot(`
Object {
"description": "Your order couldn´t be completed.",
"duration": 5000,
"status": "cancelled",
"title": "Order cancelled",
}
`);
const failedDetails = getNotificationDetails({
...mockSellOrder,
state: FIAT_ORDER_STATES.FAILED,
});
expect(failedDetails).toMatchInlineSnapshot(`
Object {
"description": "Your order couldn´t be completed.",
"duration": 5000,
"status": "error",
"title": "Order failed",
}
`);

const completedDetails = getNotificationDetails({
...mockSellOrder,
state: FIAT_ORDER_STATES.COMPLETED,
});
expect(completedDetails).toMatchInlineSnapshot(`
Object {
"description": "Your order was successful!.",
"duration": 5000,
"status": "success",
"title": "Order completed",
}
`);
const createdDetails = getNotificationDetails({
...mockSellOrder,
state: FIAT_ORDER_STATES.CREATED,
});
expect(createdDetails).toMatchInlineSnapshot(`null`);
});
});
132 changes: 132 additions & 0 deletions app/components/UI/Ramp/common/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
import { RampType } from '../types';
import { getOrders, FiatOrder } from '../../../../../reducers/fiatOrders';
import { RootState } from '../../../../../reducers';
import { FIAT_ORDER_STATES } from '../../../../../constants/on-ramp';
import { strings } from '../../../../../../locales/i18n';

const isOverAnHour = (minutes: number) => minutes > 59;

Expand Down Expand Up @@ -204,3 +206,133 @@ export function isSellOrder(order: Order): order is SellOrder {
export function isSellFiatOrder(order: FiatOrder): order is FiatOrder {
return order.orderType === OrderOrderTypeEnum.Sell;
}

const NOTIFICATION_DURATION = 5000;

const baseNotificationDetails = {
duration: NOTIFICATION_DURATION,
};

/**
* @param {FiatOrder} fiatOrder
*/
export const getNotificationDetails = (fiatOrder: FiatOrder) => {
switch (fiatOrder.state) {
case FIAT_ORDER_STATES.FAILED: {
if (fiatOrder.orderType === OrderOrderTypeEnum.Buy) {
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.purchase_failed_title',
{
currency: fiatOrder.cryptocurrency,
},
),
description: strings(
'fiat_on_ramp_aggregator.notifications.purchase_failed_description',
),
status: 'error',
};
}
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.sale_failed_title',
),
description: strings(
'fiat_on_ramp_aggregator.notifications.sale_failed_description',
),
status: 'error',
};
}
case FIAT_ORDER_STATES.CANCELLED: {
if (fiatOrder.orderType === OrderOrderTypeEnum.Buy) {
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.purchase_cancelled_title',
),
description: strings(
'fiat_on_ramp_aggregator.notifications.purchase_cancelled_description',
),
status: 'cancelled',
};
}
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.sale_cancelled_title',
),
description: strings(
'fiat_on_ramp_aggregator.notifications.sale_cancelled_description',
),
status: 'cancelled',
};
}
case FIAT_ORDER_STATES.COMPLETED: {
if (fiatOrder.orderType === OrderOrderTypeEnum.Buy) {
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.purchase_completed_title',
{
amount: renderNumber(String(fiatOrder.cryptoAmount)),
currency: fiatOrder.cryptocurrency,
},
),
description: strings(
'fiat_on_ramp_aggregator.notifications.purchase_completed_description',
{
currency: fiatOrder.cryptocurrency,
},
),
status: 'success',
};
}
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.sale_completed_title',
),
description: strings(
'fiat_on_ramp_aggregator.notifications.sale_completed_description',
),
status: 'success',
};
}
case FIAT_ORDER_STATES.CREATED: {
return null;
}
case FIAT_ORDER_STATES.PENDING:
default: {
if (fiatOrder.orderType === OrderOrderTypeEnum.Buy) {
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.purchase_pending_title',
{
currency: fiatOrder.cryptocurrency,
},
),
description: strings(
'fiat_on_ramp_aggregator.notifications.purchase_pending_description',
),
status: 'pending',
};
}
return {
...baseNotificationDetails,
title: strings(
'fiat_on_ramp_aggregator.notifications.sale_pending_title',
{
currency: fiatOrder.cryptocurrency,
},
),
description: strings(
'fiat_on_ramp_aggregator.notifications.sale_pending_description',
),
status: 'pending',
};
}
}
};
Loading

0 comments on commit 444b319

Please sign in to comment.