Skip to content

Commit 9ee16bb

Browse files
author
Kirill Ageychenko
committed
feat: fetch NFT collections from explorer
1 parent 543f114 commit 9ee16bb

27 files changed

+525
-218
lines changed

Diff for: .last-build

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7a575d8f15d4803176c8918bf00f6c8437612388
1+
543f114e0977965de7f507b1dc6977bd3a07b8fd

Diff for: src/components/home-feed.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const HomeFeed = observer(() => {
4444
}
4545
setLastUpdate(Date.now());
4646
await Promise.allSettled([
47-
Currencies.fetchCurrencies(),
47+
!AppStore.isRpcOnly && Currencies.fetchCurrencies(),
4848
Wallet.fetchBalances(),
4949
Token.fetchTokens(),
5050
Nft.fetchNft(),
@@ -100,10 +100,12 @@ export const HomeFeed = observer(() => {
100100
<BannersWrapper />
101101
<Spacer height={12} />
102102
<LayoutWidget
103+
key={'export-layout'}
103104
direction="vertical"
104105
deep={true}
105106
children={[
106107
<HomeBanner
108+
key={'export-banner'}
107109
onPress={exportWallet}
108110
banner={{
109111
id: 'export_wallet',

Diff for: src/components/image-wrapper.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import BlastedImage, {BlastedImageProps} from 'react-native-blasted-image';
1414
import WebView from 'react-native-webview';
1515

16+
import {useThemeSelector} from '@app/hooks';
1617
import {isValidUrl} from '@app/utils';
1718

1819
import {First} from './ui';
@@ -26,7 +27,10 @@ const SVG_MIME_TYPE = 'data:image/svg+xml;base64,';
2627

2728
export function ImageWrapper({source, style, ...props}: ImageWrapperProps) {
2829
const [isError, setError] = useState(false);
29-
30+
const placeholder = useThemeSelector({
31+
light: require('@assets/images/nft_placeholder_light.png'),
32+
dark: require('@assets/images/nft_placeholder_dark.png'),
33+
});
3034
const fixedSource = useMemo(() => {
3135
if (!source) {
3236
return undefined;
@@ -120,7 +124,7 @@ export function ImageWrapper({source, style, ...props}: ImageWrapperProps) {
120124
<Image
121125
{...(props as ImageProps)}
122126
style={StyleSheet.flatten(style)}
123-
source={fixedSource!}
127+
source={placeholder!}
124128
/>
125129
)}
126130
<BlastedImage

Diff for: src/components/nft-viewer/nft-viewer-item-preview-large.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const NftViewerItemPreviewLarge = ({
1616
item,
1717
onPress,
1818
}: NftViewerItemPreviewExtendedProps) => {
19-
const imageUri = useNftImage(item.cached_url);
19+
const imageUri = useNftImage(item.metadata?.image || item?.cached_url);
2020
const handlePress = useCallback(() => onPress?.(item), [onPress, item]);
2121
const [layout, onLayout] = useLayout();
2222
const itemTextStyle = useMemo(

Diff for: src/components/nft-viewer/nft-viewer-item-preview-medium.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const NftViewerItemPreviewMedium = ({
1717
onPress,
1818
}: NftViewerItemPreviewExtendedProps) => {
1919
const handlePress = useCallback(() => onPress?.(item), [onPress, item]);
20-
const imageUri = useNftImage(item.cached_url);
20+
const imageUri = useNftImage(item.metadata?.image || item?.cached_url);
2121
const [layout, onLayout] = useLayout();
2222

2323
const containerStyle: ViewStyle = useMemo(

Diff for: src/components/nft-viewer/nft-viewer-item-preview-small.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const NftViewerItemPreviewSmall = ({
1616
onPress,
1717
}: NftViewerItemPreviewExtendedProps) => {
1818
const handlePress = useCallback(() => onPress?.(item), [onPress, item]);
19-
const imageUri = useNftImage(item.cached_url);
19+
const imageUri = useNftImage(item.metadata?.image || item?.cached_url);
2020
const [layout, onLayout] = useLayout();
2121

2222
const containerStyle: ViewStyle = useMemo(

Diff for: src/components/total-value-info/total-value-info.tsx

+11-9
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,12 @@ export const TotalValueInfo = observer(
8282
[activeTab],
8383
);
8484

85-
const onTabChange = useCallback((tabName: TotalValueTabNames) => {
86-
setActiveTab(tabName);
87-
}, []);
85+
const onTabChange = useCallback(
86+
(tabName: TotalValueTabNames) => {
87+
setActiveTab(tabName);
88+
},
89+
[addressList],
90+
);
8891

8992
const renderListHeader = () => (
9093
<>
@@ -120,9 +123,9 @@ export const TotalValueInfo = observer(
120123
</>
121124
);
122125

123-
const renderListEmptyComponent = useCallback(
124-
() => (
125-
<First>
126+
const renderListEmptyComponent = useCallback(() => {
127+
return (
128+
<First key={`total-value-info-tab-content-${activeTab}`}>
126129
{activeTab === TotalValueTabNames.transactions && (
127130
<TransactionEmpty />
128131
)}
@@ -147,9 +150,8 @@ export const TotalValueInfo = observer(
147150
</>
148151
)}
149152
</First>
150-
),
151-
[activeTab, tokens],
152-
);
153+
);
154+
}, [activeTab, tokens]);
153155

154156
return (
155157
<TransactionList

Diff for: src/components/transaction-list/transaction-list.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ export const TransactionList = observer(
8888
onTransactionPress,
8989
...sectionListProps
9090
}: TransactionListProps) => {
91-
Logger.log('TransactionList', 'render', {addresses});
92-
9391
/* HOOKS */
9492
const {transactions, isTransactionsLoading} = useTransactionList(addresses);
9593
const txTimestampHeadersEnabled = useRemoteConfigVar(
@@ -181,7 +179,12 @@ export const TransactionList = observer(
181179
return sectionListProps.ListEmptyComponent;
182180
}
183181
return renderListEmptyComponentDefault;
184-
}, [hideContent, renderListEmptyComponentDefault, isTransactionsLoading]);
182+
}, [
183+
hideContent,
184+
renderListEmptyComponentDefault,
185+
isTransactionsLoading,
186+
sectionListProps.ListEmptyComponent,
187+
]);
185188

186189
const renderListFooterComponent = useCallback(
187190
() => (

Diff for: src/components/transaction-nft-confirmation.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const TransactionNftConfirmation = observer(
5151
fee,
5252
}: TransactionConfirmationProps) => {
5353
const splittedTo = useMemo(() => splitAddress(to), [to]);
54-
const imageUri = useNftImage(item.metadata?.image || item.cached_url);
54+
const imageUri = useNftImage(item.metadata?.image || item?.cached_url);
5555

5656
return (
5757
<PopupContainer style={styles.container}>

Diff for: src/contexts/app.ts

+1-18
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import Keychain, {
1111
getGenericPassword,
1212
setGenericPassword,
1313
} from 'react-native-keychain';
14-
import SplashScreen from 'react-native-splash-screen';
1514
import TouchID from 'react-native-touch-id';
1615

1716
import {DEBUG_VARS} from '@app/debug-vars';
@@ -38,7 +37,6 @@ import {Cosmos} from '@app/services/cosmos';
3837
import {EventTracker} from '@app/services/event-tracker';
3938
import {HapticEffects, vibrate} from '@app/services/haptic';
4039
import {RemoteConfig} from '@app/services/remote-config';
41-
import {sleep} from '@app/utils';
4240

4341
import {showModal} from '../helpers';
4442
import {User} from '../models/user';
@@ -132,22 +130,7 @@ class App extends AsyncEventEmitter {
132130

133131
Logger.log('isBackendAvailable', isBackendAvailable);
134132
if (!isBackendAvailable) {
135-
SplashScreen.hide();
136-
await sleep(1000);
137-
return new Promise(() => {
138-
Alert.alert(
139-
'Haqq Wallet Backend Shutdown',
140-
'The backend service for Haqq Wallet is no longer maintained. Please switch to RPC mode.',
141-
[
142-
{
143-
text: 'Switch to RPC',
144-
onPress: () => {
145-
AppStore.dataFetchMode = DataFetchSource.Rpc;
146-
},
147-
},
148-
],
149-
);
150-
});
133+
AppStore.dataFetchMode = DataFetchSource.Rpc;
151134
}
152135
}
153136
});

Diff for: src/hooks/nft/use-nft-image.ts

+4
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,9 @@ export const useNftImage = (imageUrl?: string | null): ImageURISource => {
1212
return placeholder;
1313
}
1414

15+
if (imageUrl.startsWith('ipfs://')) {
16+
return {uri: imageUrl.replace('ipfs://', 'https://ipfs.io/ipfs/')};
17+
}
18+
1519
return {uri: imageUrl};
1620
};

Diff for: src/models/app/app.store.ts

+3
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class AppStore {
9292
}
9393

9494
get isRpcOnly() {
95+
if (Provider?.selectedProvider?.isTestnet) {
96+
return true;
97+
}
9598
return this.dataFetchMode === DataFetchSource.Rpc;
9699
}
97100

Diff for: src/models/contract/contract.store.ts

+6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import {makePersistable} from 'mobx-persist-store';
44
import {AddressUtils} from '@app/helpers/address-utils';
55
import {Indexer, IndexerAddressesResponse} from '@app/services/indexer';
66
import {storage} from '@app/services/mmkv';
7+
import {fetchIndexerContract} from '@app/services/rpc/evm-contract';
78
import {AddressEthereum, AddressWallet, ChainId} from '@app/types';
89

910
import {ContractStoreData, IndexerContract} from './contract.types';
1011

12+
import {AppStore} from '../app';
1113
import {ALL_NETWORKS_ID, Provider} from '../provider';
1214

1315
class Contract {
@@ -121,6 +123,10 @@ class Contract {
121123
}
122124
}
123125

126+
if (!contract && AppStore.isRpcOnly) {
127+
return fetchIndexerContract(AddressUtils.toEth(contractAddress));
128+
}
129+
124130
return contract;
125131
};
126132
}

Diff for: src/models/nft/nft.store.ts

+32-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {makeAutoObservable, runInAction, when} from 'mobx';
2+
import {makePersistable} from 'mobx-persist-store';
23

34
import {
45
ContractType,
@@ -10,21 +11,30 @@ import {
1011
import {Socket} from '@app/models/socket';
1112
import {Wallet} from '@app/models/wallet';
1213
import {Indexer} from '@app/services/indexer';
14+
import {storage} from '@app/services/mmkv';
1315
import {AddressCosmosHaqq} from '@app/types';
1416
import {RPCMessage} from '@app/types/rpc';
1517

18+
import {AppStore} from '../app';
1619
import {Contract, IndexerContract} from '../contract';
1720

1821
class NftStore {
1922
data: Record<AddressCosmosHaqq, NftCollection> = {};
2023

2124
constructor() {
2225
makeAutoObservable(this);
26+
makePersistable(this, {
27+
name: this.constructor.name,
28+
properties: ['data'],
29+
storage,
30+
});
2331

24-
when(
25-
() => Socket.lastMessage.type === 'nft',
26-
() => this.onMessage(Socket.lastMessage),
27-
);
32+
if (!AppStore.isRpcOnly) {
33+
when(
34+
() => Socket.lastMessage.type === 'nft',
35+
() => this.onMessage(Socket.lastMessage),
36+
);
37+
}
2838
}
2939

3040
create(item: NftItem) {
@@ -158,29 +168,33 @@ class NftStore {
158168
};
159169

160170
private parseIndexerNfts = (data: NftCollectionIndexer[]): void => {
161-
this.data = {};
162-
163171
data.forEach(async item => {
164172
const contract = await Contract.getById(item.address, item.chain_id);
165173

174+
const tmpData: typeof this.data = {};
166175
if (contract) {
167176
const contractType = contract.is_erc721
168177
? ContractType.erc721
169178
: ContractType.erc1155;
170179

171-
runInAction(() => {
172-
this.data[item.id] = {
173-
...item,
174-
description: item.description || '',
175-
created_at: Date.now(),
176-
contractType: contractType,
177-
is_transfer_prohibinden: Boolean(contract.is_transfer_prohibinden),
178-
nfts: item.nfts
179-
.map(nft => this.parseIndexerNft(nft, contract))
180-
.filter(i => i !== null),
181-
};
182-
});
180+
tmpData[item.id] = {
181+
...item,
182+
description: item.description || '',
183+
created_at: Date.now(),
184+
contractType: contractType,
185+
is_transfer_prohibinden: Boolean(contract.is_transfer_prohibinden),
186+
nfts: item.nfts
187+
.map(nft => this.parseIndexerNft(nft, contract))
188+
.filter(i => i !== null),
189+
};
183190
}
191+
192+
runInAction(() => {
193+
this.data = {
194+
...this.data,
195+
...tmpData,
196+
};
197+
});
184198
});
185199
};
186200

Diff for: src/models/provider/provider.ts

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {makePersistable} from '@override/mobx-persist-store';
22
import {makeAutoObservable, runInAction} from 'mobx';
3-
import Config from 'react-native-config';
43

54
import {Events} from '@app/events';
65
import {hideModal, showModal} from '@app/helpers';
@@ -11,11 +10,7 @@ import {storage} from '@app/services/mmkv';
1110
import {WalletConnect} from '@app/services/wallet-connect';
1211
import {ModalType} from '@app/types';
1312
import {createAsyncTask} from '@app/utils';
14-
import {
15-
DEFAULT_PROVIDERS,
16-
MAIN_NETWORK_ID,
17-
TEST_NETWORK_ID,
18-
} from '@app/variables/common';
13+
import {DEFAULT_PROVIDERS} from '@app/variables/common';
1914

2015
import {RemoteProviderConfig} from './provider-config';
2116
import {ProviderModel} from './provider.model';
@@ -46,10 +41,9 @@ class ProviderStore {
4641
awaitForInitialization() {
4742
throw new Error('Method not implemented.');
4843
}
49-
private _defaultProviderId =
50-
Config.ENVIRONMENT === 'production' || Config.ENVIRONMENT === 'distribution'
51-
? MAIN_NETWORK_ID
52-
: TEST_NETWORK_ID;
44+
private _defaultProviderId = DEFAULT_PROVIDERS.find(
45+
it => it.stage === 'mainnet',
46+
)?.id!;
5347

5448
_selectedProviderId: ProviderID = this._defaultProviderId;
5549
_data: Record<ProviderID, ProviderModel> = {};

Diff for: src/models/tokens.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,12 @@ class TokensStore implements MobXStore<IToken> {
135135
storage,
136136
});
137137

138-
when(
139-
() => Socket.lastMessage.type === 'token',
140-
() => this.onMessage(Socket.lastMessage),
141-
);
138+
if (!AppStore.isRpcOnly) {
139+
when(
140+
() => Socket.lastMessage.type === 'token',
141+
() => this.onMessage(Socket.lastMessage),
142+
);
143+
}
142144
}
143145

144146
get isLoading() {

0 commit comments

Comments
 (0)