Skip to content

Commit

Permalink
feat: support hide scam transactions (#1811)
Browse files Browse the repository at this point in the history
* feat: filter scam

* chore: i18n

* feat: update rabby-api

* chore: change hover style

* fix: i18n
  • Loading branch information
cs1707 authored Oct 20, 2023
1 parent 59df496 commit 1a51ca1
Show file tree
Hide file tree
Showing 14 changed files with 323 additions and 139 deletions.
5 changes: 5 additions & 0 deletions _raw/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
},
"modalViewMessage": {
"title": "View Message"
},
"filterScam": {
"title": "Hide scam transactions",
"loading": "Loading may take a moment, and data delays are possible",
"btn": "Hide scam transactions"
}
},
"chainList": {
Expand Down
5 changes: 5 additions & 0 deletions _raw/locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
"txHistory": {
"tipInputData": "这笔交易有留言",
"parseInputDataError": "解析留言失败"
},
"filterScam": {
"title": "隐藏欺诈交易",
"loading": "数据加载需要一定时间,且可能会有延迟",
"btn": "隐藏欺诈交易"
}
},
"dashboard": {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"@rabby-wallet/eth-walletconnect-keyring": "^2.0.4-beta.0",
"@rabby-wallet/eth-watch-keyring": "^1.0.0",
"@rabby-wallet/gnosis-sdk": "^1.3.5",
"@rabby-wallet/rabby-api": "^0.6.24",
"@rabby-wallet/page-provider": "^0.1.23",
"@rabby-wallet/rabby-api": "^0.6.23",
"@rabby-wallet/rabby-security-engine": "^1.1.16",
"@rabby-wallet/rabby-swap": "^0.0.29",
"@rabby-wallet/widgets": "^1.0.9",
Expand Down Expand Up @@ -108,6 +108,7 @@
"react-redux": "^8.0.1",
"react-router-dom": "^5.2.0",
"react-use": "^17.2.4",
"react-virtuoso": "^4.6.1",
"react-window": "^1.8.6",
"recharts": "^2.7.1",
"redux": "^4.2.0",
Expand Down
3 changes: 3 additions & 0 deletions src/ui/assets/history/icon-arrow-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 9 additions & 2 deletions src/ui/component/TxHistory/TokenLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import clsx from 'clsx';
import React from 'react';
import NFTModal from '@/ui/views/Dashboard/components/NFT/NFTModal';
import { Modal } from 'antd';
import { useTranslation } from 'react-i18next';

export interface Props {
token: TokenItem;
Expand All @@ -20,6 +21,12 @@ export const TokenLabel: React.FC<Props> = ({
onClose,
}) => {
const [visible, setVisible] = React.useState(false);
const { t } = useTranslation();
const symbol = getTokenSymbol(token);
const name = isNft
? token?.name ||
(symbol ? `${symbol} ${token?.inner_id}` : t('global.unknownNFT'))
: symbol;

return (
<>
Expand All @@ -28,11 +35,11 @@ export const TokenLabel: React.FC<Props> = ({
if (!canClickToken) return;
setVisible(true);
}}
className={clsx('ml-2', {
className={clsx('ml-2', 'truncate', {
'underline cursor-pointer': canClickToken,
})}
>
{getTokenSymbol(token)}
{name}
</span>
{isNft ? (
<Modal
Expand Down
9 changes: 7 additions & 2 deletions src/ui/component/TxHistory/TxChange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export const TokenChange = ({
return (
<div className="ui token-change">
{info.sends?.map((v) => {
const token = tokens[v.token_id];
const tokenId = v.token_id;
const tokenUUID = `${info.chain}_token:${tokenId}`;
const token = tokens[tokenId] || tokens[tokenUUID];
const isNft = v.token_id?.length === 32;
const symbol = getTokenSymbol(token);
const name = isNft
Expand Down Expand Up @@ -81,7 +83,10 @@ export const TokenChange = ({
);
})}
{info.receives?.map((v) => {
const token = tokens[v.token_id];
const tokenId = v.token_id;
const tokenUUID = `${info.chain}_token:${tokenId}`;

const token = tokens[tokenId] || tokens[tokenUUID];
const isNft = v.token_id?.length === 32;
const symbol = getTokenSymbol(token);
const name = isNft
Expand Down
6 changes: 5 additions & 1 deletion src/ui/component/TxHistory/TxInterAddressExplain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export const TxInterAddressExplain = ({
if (isCancel) {
interAddressExplain = t('page.transactions.explain.cancel');
} else if (isApprove) {
const approveToken = tokenDict[data.token_approve?.token_id || ''];
const tokenId = data.token_approve?.token_id || '';
const tokenUUID = `${data.chain}_token:${tokenId}`;

const approveToken = tokenDict[tokenId] || tokenDict[tokenUUID];

const amount = data.token_approve?.value || 0;

// todo: translate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { TokenChange, TxId, TxInterAddressExplain } from '@/ui/component';
import { useTranslation } from 'react-i18next';
import { useAsync } from 'react-use';

import IconInputData from './icons/input-data.svg';
import IconInputData from '../icons/input-data.svg';
import { useRabbySelector } from '@/ui/store';
import { Skeleton, Tooltip } from 'antd';
import { AddressType } from '@/ui/utils/address';
Expand Down Expand Up @@ -178,10 +178,20 @@ function useClientParseTx({
data.cate_id &&
['send', 'receive'].includes(data.cate_id) &&
((!data.receives.length && !data.receives.length) ||
data.receives?.filter((v) => isTokenItemNative(tokenDict[v.token_id]))
.length === 1 ||
data.sends?.filter((v) => isTokenItemNative(tokenDict[v.token_id]))
.length === 1)
data.receives?.filter((v) => {
const tokenId = v.token_id;
const tokenUUID = `${data.chain}_token:${tokenId}`;
return isTokenItemNative(
tokenDict[v.token_id] || tokenDict[tokenUUID]
);
}).length === 1 ||
data.sends?.filter((v) => {
const tokenId = v.token_id;
const tokenUUID = `${data.chain}_token:${tokenId}`;
return isTokenItemNative(
tokenDict[v.token_id] || tokenDict[tokenUUID]
);
}).length === 1)
);
}, [data, chainItem?.nativeTokenSymbol, tokenDict]);

Expand Down
172 changes: 172 additions & 0 deletions src/ui/views/History/components/HistoryList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { last } from 'lodash';
import React, { useRef } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';

import { useAccount } from '@/ui/store-hooks';
import { useInfiniteScroll } from 'ahooks';
import { Virtuoso } from 'react-virtuoso';
import { Empty, Modal } from 'ui/component';
import { useWallet } from 'ui/utils';
import { HistoryItem, HistoryItemActionContext } from './HistoryItem';
import { Loading } from './Loading';

const PAGE_COUNT = 10;

export const HistoryList = ({
isMainnet = true,
isFilterScam = false,
}: {
isMainnet?: boolean;
isFilterScam?: boolean;
}) => {
const wallet = useWallet();
const { t } = useTranslation();

const ref = useRef<HTMLDivElement | null>(null);
const [account] = useAccount();

const getAllTxHistory = (
params: Parameters<typeof wallet.openapi.getAllTxHistory>[0]
) => {
const getHistory = isMainnet
? wallet.openapi.getAllTxHistory
: wallet.testnetOpenapi.getAllTxHistory;

return getHistory(params).then((res) => {
if (res.history_list) {
res.history_list = res.history_list.filter((item) => {
return !item.is_scam;
});
}
return res;
});
};

const fetchData = async (startTime = 0) => {
const { address } = account!;

const getHistory = isFilterScam
? getAllTxHistory
: isMainnet
? wallet.openapi.listTxHisotry
: wallet.testnetOpenapi.listTxHisotry;

const res = await getHistory({
id: address,
start_time: !isFilterScam ? startTime : undefined,
});

const { project_dict, cate_dict, history_list: list } = res;
const displayList = list
.map((item) => ({
...item,
projectDict: project_dict,
cateDict: cate_dict,
tokenDict: 'token_dict' in res ? res.token_dict : undefined,
tokenUUIDDict:
'token_uuid_dict' in res ? res.token_uuid_dict : undefined,
}))
.sort((v1, v2) => v2.time_at - v1.time_at);
return {
last: last(displayList)?.time_at,
list: displayList,
};
};

const { data, loading, loadingMore, loadMore } = useInfiniteScroll(
(d) => fetchData(d?.last),
{
isNoMore: (d) => {
return isFilterScam
? true
: !d?.last || (d?.list.length || 0) < PAGE_COUNT;
},
}
);

const isEmpty = (data?.list?.length || 0) <= 0 && !loading;

const [
focusingHistoryItem,
setFocusingHistoryItem,
] = React.useState<HistoryItemActionContext | null>(null);

return (
<div className="overflow-auto h-full" ref={ref}>
<Modal
visible={!!focusingHistoryItem}
// View Message
title={t('page.transactions.modalViewMessage.title')}
className="view-tx-message-modal"
onCancel={() => {
setFocusingHistoryItem(null);
}}
maxHeight="360px"
>
<div className="parsed-content text-14">
{focusingHistoryItem?.parsedInputData}
</div>
</Modal>

{loading ? (
<div className={isFilterScam ? 'pt-[20px]' : ''}>
{isFilterScam ? (
<div className="filter-scam-loading-text">
{t('page.transactions.filterScam.loading')}
</div>
) : null}
<Loading count={4} active />
</div>
) : (
<>
{isEmpty ? (
<Empty
title={t('page.transactions.empty.title')}
desc={
<span>
<Trans i18nKey="page.transactions.empty.desc" t={t}>
No transactions found on
<Link className="underline" to="/settings/chain-list">
supported chains
</Link>
</Trans>
</span>
}
className="pt-[108px]"
></Empty>
) : (
<Virtuoso
style={{
height: '100%',
}}
data={data?.list || []}
itemContent={(_, item) => {
return (
<HistoryItem
data={item}
projectDict={item.projectDict}
cateDict={item.cateDict}
tokenDict={item.tokenDict || item.tokenUUIDDict || {}}
key={item.id}
onViewInputData={setFocusingHistoryItem}
isTestnet={!isMainnet}
/>
);
}}
endReached={loadMore}
components={{
Footer: () => {
if (loadingMore) {
return <Loading count={4} active />;
}
return null;
},
}}
></Virtuoso>
)}
</>
)}
</div>
);
};
File renamed without changes.
Loading

0 comments on commit 1a51ca1

Please sign in to comment.