Skip to content

Commit

Permalink
fix(mobile): refund && scam fixes (#883)
Browse files Browse the repository at this point in the history
* fix(mobile): Button colors

* fix(mobile): Mark transactions with spam NFTs, update naming
  • Loading branch information
voloshinskii authored May 24, 2024
1 parent dce544a commit e1c06ea
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 144 deletions.
2 changes: 1 addition & 1 deletion packages/mobile/src/core/NFT/NFT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export const NFT: React.FC<NFTProps> = ({ oldNftItem, route }) => {

if (approvalStatus === TokenApprovalStatus.Spam) {
if (!tk.wallet.isTestnet) {
fetch(`${config.get('scamEndpoint')}/report/${address}`, {
fetch(`${config.get('scamEndpoint')}/report/${nft.address}`, {
method: 'POST',
}).catch((e) => console.warn(e));
}
Expand Down
9 changes: 6 additions & 3 deletions packages/shared/components/ActivityList/ActionListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ActionListItemProps<TActionType extends ActionType = ActionType
disablePressable?: boolean;
disableNftPreview?: boolean;
isSimplePreview?: boolean;
isScam?: boolean;
}

export const ActionListItem = memo<ActionListItemProps>((props) => {
Expand All @@ -60,13 +61,15 @@ export const ActionListItem = memo<ActionListItemProps>((props) => {
ignoreFailed,
disablePressable,
isSimplePreview,
isScam: isScamProp,
} = props;
const { formatNano } = useHideableFormatter();

const isScam =
action.event.is_scam ||
(isJettonTransferAction(action) &&
action.payload.jetton.verification === JettonVerificationType.Blacklist);
isScamProp ??
(action.event.is_scam ||
(isJettonTransferAction(action) &&
action.payload.jetton.verification === JettonVerificationType.Blacklist));

const flags = useFlags(['address_style_nobounce']);

Expand Down
26 changes: 2 additions & 24 deletions packages/shared/components/ActivityList/ActionListItemByType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ActionType,
AnyActionItem,
} from '@tonkeeper/mobile/src/wallet/models/ActivityModel';
import { ActionListItemWithNft } from './ActionListItemWithNFT';

export const ActionListItemByType = memo<ActionListItemProps>((props) => {
const { action } = props;
Expand Down Expand Up @@ -50,31 +51,8 @@ export const ActionListItemByType = memo<ActionListItemProps>((props) => {
</ActionListItem>
);
case ActionType.NftItemTransfer:
return (
<ActionListItem {...pureProps} value="NFT">
<NftPreviewContent
disabled={props.disableNftPreview || props.disablePressable}
nftAddress={payload.nft}
/>
{!!payload.comment && <ListItemContentText text={payload.comment.trim()} />}
{!!payload.encrypted_comment && (
<ListItemEncryptedComment
encryptedComment={payload.encrypted_comment}
actionId={action.action_id}
sender={action.payload.sender!}
/>
)}
</ActionListItem>
);
case ActionType.NftPurchase:
return (
<ActionListItem {...pureProps}>
<NftPreviewContent
disabled={props.disableNftPreview || props.disablePressable}
nftItem={payload.nft}
/>
</ActionListItem>
);
return <ActionListItemWithNft {...pureProps} action={action} />;
case ActionType.SmartContractExec:
return (
<ActionListItem
Expand Down
71 changes: 71 additions & 0 deletions packages/shared/components/ActivityList/ActionListItemWithNFT.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { memo } from 'react';
import { NftPreviewContent } from './NftPreviewContent';
import { ListItemContentText } from '@tonkeeper/uikit';
import { ListItemEncryptedComment } from '@tonkeeper/uikit/src/components/List/ListItemEncryptedComment';
import { ActionListItem, ActionListItemProps } from './ActionListItem';
import { ActionType } from '@tonkeeper/mobile/src/wallet/models/ActivityModel';
import { useNftItemByAddress } from '../../query/hooks/useNftItemByAddress';
import { TrustType } from '@tonkeeper/core/src/TonAPI';
import { Address } from '../../Address';
import { TokenApprovalStatus } from '@tonkeeper/mobile/src/wallet/managers/TokenApprovalManager';
import { useTokenApproval } from '../../hooks';

export interface ActionListItemWithNftProps
extends ActionListItemProps<ActionType.NftPurchase | ActionType.NftItemTransfer> {}

export const ActionListItemWithNft = memo<ActionListItemWithNftProps>((props) => {
const { action } = props;
const { payload, type } = action;

const nftAddress = type === ActionType.NftPurchase ? payload.nft.address : payload.nft;
const nftItem = type === ActionType.NftPurchase ? payload.nft : undefined;

const { data: nft } = useNftItemByAddress(nftAddress, {
existingNft: nftItem,
});

const approvalStatuses = useTokenApproval((state) => state.tokens);
const approvalIdentifier = nft
? Address.parse(nft?.collection?.address ?? nft?.address).toRaw()
: '';
const nftApprovalStatus = approvalStatuses[approvalIdentifier];

const isScam =
nft &&
((nft.trust === TrustType.Blacklist &&
nftApprovalStatus?.current !== TokenApprovalStatus.Approved) ||
nftApprovalStatus?.current === TokenApprovalStatus.Spam);

switch (type) {
case ActionType.NftItemTransfer:
return (
<ActionListItem {...props} isScam={isScam}>
{nft && !isScam && (
<NftPreviewContent
nft={nft}
disabled={props.disableNftPreview || props.disablePressable}
/>
)}
{!!payload.comment && <ListItemContentText text={payload.comment.trim()} />}
{!!payload.encrypted_comment && (
<ListItemEncryptedComment
encryptedComment={payload.encrypted_comment}
actionId={action.action_id}
sender={action.payload.sender!}
/>
)}
</ActionListItem>
);
case ActionType.NftPurchase:
return (
<ActionListItem {...props} isScam={isScam}>
{nft && !isScam && (
<NftPreviewContent
nft={nft}
disabled={props.disableNftPreview || props.disablePressable}
/>
)}
</ActionListItem>
);
}
});
163 changes: 69 additions & 94 deletions packages/shared/components/ActivityList/NftPreviewContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useNftItemByAddress } from '../../query/hooks/useNftItemByAddress';
import { Steezy, View, Text, Icon, Picture, ListItemContent } from '@tonkeeper/uikit';
import { NftItem, TrustType } from '@tonkeeper/core/src/TonAPI';
import { TrustType } from '@tonkeeper/core/src/TonAPI';
import { Pressable } from 'react-native';
import { memo, useCallback } from 'react';
import { t } from '../../i18n';
Expand All @@ -18,16 +17,12 @@ import { useTokenApproval } from '../../hooks';
import { TokenApprovalStatus } from '@tonkeeper/mobile/src/wallet/managers/TokenApprovalManager';

interface NftPreviewContentProps {
nftAddress?: string;
nftItem?: NftItem;
nft: any;
disabled?: boolean;
}

export const NftPreviewContent = memo<NftPreviewContentProps>((props) => {
const { nftAddress, nftItem, disabled } = props;
const { data: nft } = useNftItemByAddress(props.nftAddress, {
existingNft: nftItem,
});
const { nft, disabled } = props;

const approvalStatuses = useTokenApproval((state) => state.tokens);
const hideableAnimProgress = useHideableAmount();
Expand All @@ -37,99 +32,79 @@ export const NftPreviewContent = memo<NftPreviewContentProps>((props) => {
}));

const handlePress = useCallback(() => {
const address = nftAddress ?? nftItem?.address;
const address = nft.address;
if (address) {
openNftModal(address);
}
}, [nftAddress, nftItem?.address]);
}, [nft.address]);

if (nft) {
const approvalIdentifier = Address.parse(
nft?.collection?.address ?? nft?.address,
).toRaw();
const nftApprovalStatus = approvalStatuses[approvalIdentifier];
const approvalIdentifier = Address.parse(
nft?.collection?.address ?? nft?.address,
).toRaw();
const nftApprovalStatus = approvalStatuses[approvalIdentifier];

const isDisabled =
disabled || nftApprovalStatus?.current === TokenApprovalStatus.Spam;

if (
nftApprovalStatus?.current === TokenApprovalStatus.Spam ||
(nft?.trust === TrustType.Blacklist &&
nftApprovalStatus?.current !== TokenApprovalStatus.Approved)
) {
return null;
}

return (
<Pressable
disabled={isDisabled}
onPress={handlePress}
style={styles.container.static}
>
<ListItemContent style={styles.item.static}>
<View style={styles.pictureContainer}>
<HideableImage
imageStyle={styles.picture}
image={
<Picture
preview={nft.image?.preview}
uri={nft.image?.small}
style={styles.picture}
/>
}
/>
</View>
<View style={styles.infoContainer}>
<HideableAmount
animationDirection={AnimationDirection.Left}
stars="* * * *"
variant="body2"
numberOfLines={1}
>
{nft.name}
</HideableAmount>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{nft.trust === TrustType.None ? (
<Text
numberOfLines={1}
type="body2"
color={
nftApprovalStatus?.current === TokenApprovalStatus.Approved
? 'textSecondary'
: 'accentOrange'
}
>
{t('suspicious.label.full')}
</Text>
) : (
<HideableAmount
animationDirection={AnimationDirection.Left}
variant="body2"
color="textSecondary"
numberOfLines={1}
>
{nft.collection?.name || t('nft_single_nft')}
</HideableAmount>
)}
{nft.approved_by?.length > 0 && (
<Animated.View
style={[styles.verificationIcon.static, hideableAnimStyle]}
>
<Icon name="ic-verification-secondary-16" color="iconSecondary" />
</Animated.View>
)}
</View>
</View>
</ListItemContent>
</Pressable>
);
}
const isDisabled = disabled || nftApprovalStatus?.current === TokenApprovalStatus.Spam;

return (
<ListItemContent style={styles.item.static}>
<View style={styles.pictureContainer} />
<View style={styles.infoContainer} />
</ListItemContent>
<Pressable
disabled={isDisabled}
onPress={handlePress}
style={styles.container.static}
>
<ListItemContent style={styles.item.static}>
<View style={styles.pictureContainer}>
<HideableImage
imageStyle={styles.picture}
image={
<Picture
preview={nft.image?.preview}
uri={nft.image?.small}
style={styles.picture}
/>
}
/>
</View>
<View style={styles.infoContainer}>
<HideableAmount
animationDirection={AnimationDirection.Left}
stars="* * * *"
variant="body2"
numberOfLines={1}
>
{nft.name}
</HideableAmount>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{nft.trust === TrustType.None ? (
<Text
numberOfLines={1}
type="body2"
color={
nftApprovalStatus?.current === TokenApprovalStatus.Approved
? 'textSecondary'
: 'accentOrange'
}
>
{t('suspicious.label.full')}
</Text>
) : (
<HideableAmount
animationDirection={AnimationDirection.Left}
variant="body2"
color="textSecondary"
numberOfLines={1}
>
{nft.collection?.name || t('nft_single_nft')}
</HideableAmount>
)}
{nft.approved_by?.length > 0 && (
<Animated.View style={[styles.verificationIcon.static, hideableAnimStyle]}>
<Icon name="ic-verification-secondary-16" color="iconSecondary" />
</Animated.View>
)}
</View>
</View>
</ListItemContent>
</Pressable>
);
});

Expand Down
29 changes: 18 additions & 11 deletions packages/shared/components/RefillBattery/RestorePurchases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import { Platform } from 'react-native';
import { tk } from '@tonkeeper/mobile/src/wallet';
import { openDAppBrowser } from '@tonkeeper/mobile/src/navigation';
import { config } from '@tonkeeper/mobile/src/config';
import { useExternalState } from '../../hooks/useExternalState';

export const RestorePurchases = memo(() => {
const reservedBalance = useExternalState(
tk.wallet.battery.state,
(state) => state.reservedBalance,
);
const handleRestorePurchases = useCallback(async () => {
try {
const purchases = await getPendingPurchasesIOS();
Expand Down Expand Up @@ -75,22 +80,24 @@ export const RestorePurchases = memo(() => {
</Text>
.
</Text>
<Text
style={styles.text.static}
type="body2"
textAlign="center"
color="textTertiary"
>
{!(!reservedBalance || reservedBalance === '0') && (
<Text
onPress={openRefundsDApp}
style={styles.text.static}
type="body2"
textAlign="center"
color="textSecondary"
color="textTertiary"
>
{t('battery.packages.refund')}
<Text
onPress={openRefundsDApp}
type="body2"
textAlign="center"
color="textSecondary"
>
{t('battery.packages.refund')}
</Text>
.
</Text>
.
</Text>
)}
</>
);
});
Expand Down
Loading

0 comments on commit e1c06ea

Please sign in to comment.