Skip to content

Commit 543f114

Browse files
author
Kirill Ageychenko
committed
feat: automaticly switch to backend
1 parent 13a0dec commit 543f114

File tree

9 files changed

+165
-35
lines changed

9 files changed

+165
-35
lines changed

src/contexts/app.ts

+75-22
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import {appleAuth} from '@invertase/react-native-apple-authentication';
33
import dynamicLinks from '@react-native-firebase/dynamic-links';
44
import {GoogleSignin} from '@react-native-google-signin/google-signin';
55
import {subMinutes} from 'date-fns';
6+
import {when} from 'mobx';
67
import {Alert, AppState, Appearance, Platform, StatusBar} from 'react-native';
78
import Config from 'react-native-config';
89
import Keychain, {
910
STORAGE_TYPE,
1011
getGenericPassword,
1112
setGenericPassword,
1213
} from 'react-native-keychain';
14+
import SplashScreen from 'react-native-splash-screen';
1315
import TouchID from 'react-native-touch-id';
1416

1517
import {DEBUG_VARS} from '@app/debug-vars';
@@ -19,6 +21,7 @@ import {Events} from '@app/events';
1921
import {AsyncEventEmitter} from '@app/helpers/async-event-emitter';
2022
import {awaitForEventDone} from '@app/helpers/await-for-event-done';
2123
import {checkNeedUpdate} from '@app/helpers/check-app-version';
24+
import {isConnected} from '@app/helpers/check-internet';
2225
import {getRpcProvider} from '@app/helpers/get-rpc-provider';
2326
import {getUid} from '@app/helpers/get-uid';
2427
import {SecurePinUtils} from '@app/helpers/secure-pin-utils';
@@ -35,12 +38,14 @@ import {Cosmos} from '@app/services/cosmos';
3538
import {EventTracker} from '@app/services/event-tracker';
3639
import {HapticEffects, vibrate} from '@app/services/haptic';
3740
import {RemoteConfig} from '@app/services/remote-config';
41+
import {sleep} from '@app/utils';
3842

3943
import {showModal} from '../helpers';
4044
import {User} from '../models/user';
4145
import {
4246
AppTheme,
4347
BiometryType,
48+
DataFetchSource,
4449
DynamicLink,
4550
MarketingEvents,
4651
ModalType,
@@ -110,29 +115,77 @@ class App extends AsyncEventEmitter {
110115

111116
this.user = User.getOrCreate();
112117

113-
Provider.init()
114-
.then(RemoteProviderConfig.init)
115-
.then(() => {
116-
EthNetwork.init(Provider.selectedProvider);
117-
118-
this.checkBalance();
119-
120-
this.handleDynamicLink = this.handleDynamicLink.bind(this);
121-
122-
dynamicLinks().onLink(this.handleDynamicLink);
123-
dynamicLinks().getInitialLink().then(this.handleDynamicLink);
124-
125-
this.listenTheme = this.listenTheme.bind(this);
126-
127-
Appearance.addChangeListener(this.listenTheme);
128-
AppState.addEventListener('change', this.listenTheme);
129-
this.listenTheme();
130-
AppState.addEventListener('change', this.onAppStatusChanged.bind(this));
118+
when(
119+
() => AppStore.isHydrated,
120+
async () => {
121+
if (AppStore.dataFetchMode === DataFetchSource.Backend) {
122+
await isConnected().then(async connected => {
123+
if (connected) {
124+
let isBackendAvailable = true;
125+
126+
try {
127+
await Backend.instance.news(new Date());
128+
Logger.log('Backend is available');
129+
} catch {
130+
isBackendAvailable = false;
131+
}
132+
133+
Logger.log('isBackendAvailable', isBackendAvailable);
134+
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+
});
151+
}
152+
}
153+
});
154+
}
131155

132-
this.setEnabledLoggersForTestMode();
133-
this.stopInitialization();
134-
})
135-
.catch(this.stopInitialization);
156+
Logger.log('AppStore isHydrated done');
157+
Provider.init()
158+
.then(RemoteProviderConfig.init)
159+
.then(() => {
160+
EthNetwork.init(Provider.selectedProvider);
161+
162+
this.checkBalance();
163+
164+
this.handleDynamicLink = this.handleDynamicLink.bind(this);
165+
166+
dynamicLinks().onLink(this.handleDynamicLink);
167+
dynamicLinks().getInitialLink().then(this.handleDynamicLink);
168+
169+
this.listenTheme = this.listenTheme.bind(this);
170+
171+
Appearance.addChangeListener(this.listenTheme);
172+
AppState.addEventListener('change', this.listenTheme);
173+
this.listenTheme();
174+
AppState.addEventListener(
175+
'change',
176+
this.onAppStatusChanged.bind(this),
177+
);
178+
179+
this.setEnabledLoggersForTestMode();
180+
this.stopInitialization();
181+
})
182+
.catch(e => {
183+
Logger.error(e, 'App init error');
184+
this.stopInitialization();
185+
})
186+
.finally(this.stopInitialization);
187+
},
188+
);
136189
}
137190

138191
private _startUpTime: number;

src/event-actions/on-deep-link.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ import {VariablesBool} from '@app/models/variables-bool';
1212
import {Wallet} from '@app/models/wallet';
1313
import {Whitelist} from '@app/models/whitelist';
1414
import {sendNotification} from '@app/services/toast';
15-
import {DeeplinkProtocol, DeeplinkUrlKey, ModalType} from '@app/types';
15+
import {
16+
DataFetchSource,
17+
DeeplinkProtocol,
18+
DeeplinkUrlKey,
19+
ModalType,
20+
} from '@app/types';
1621
import {openInAppBrowser, openWeb3Browser} from '@app/utils';
1722

1823
import {onDynamicLink} from './on-dynamic-link';
@@ -161,6 +166,9 @@ export async function onDeepLink(
161166
case DeeplinkUrlKey.export:
162167
await exportWallet();
163168
return true;
169+
case DeeplinkUrlKey.useRpc:
170+
AppStore.dataFetchMode = DataFetchSource.Rpc;
171+
return true;
164172
}
165173
}
166174
} catch (error) {

src/helpers/check-internet.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const endpoints: string[] = [
2+
'https://www.google.com/generate_204',
3+
'https://connectivitycheck.gstatic.com/generate_204',
4+
'https://captive.apple.com/generate_204',
5+
'https://www.cloudflare.com/cdn-cgi/trace',
6+
];
7+
8+
async function fetchWithTimeout(
9+
url: string,
10+
timeout = 5000,
11+
): Promise<Response> {
12+
const controller = new AbortController();
13+
const id = setTimeout(() => controller.abort(), timeout);
14+
try {
15+
const response = await fetch(url, {signal: controller.signal});
16+
return response;
17+
} finally {
18+
clearTimeout(id);
19+
}
20+
}
21+
22+
export async function isConnected(): Promise<boolean> {
23+
try {
24+
await Promise.any(endpoints.map(url => fetchWithTimeout(url)));
25+
return true;
26+
} catch (error) {
27+
return false;
28+
}
29+
}

src/models/app/app.store.ts

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1-
import {isHydrated, makePersistable} from '@override/mobx-persist-store';
1+
import {makePersistable} from '@override/mobx-persist-store';
22
import {makeAutoObservable, runInAction} from 'mobx';
3+
import {Alert} from 'react-native';
34
import Config from 'react-native-config';
45
import {
56
startNetworkLogging,
67
stopNetworkLogging,
78
} from 'react-native-network-logger';
9+
import RNRestart from 'react-native-restart';
810

911
import {storage} from '@app/services/mmkv';
1012
import {DataFetchSource} from '@app/types';
1113

14+
import {isHydrated} from './../../overrides/mobx-persist-store/index';
15+
16+
import {Provider} from '../provider';
17+
import {Wallet} from '../wallet';
18+
1219
class AppStore {
1320
// App session properties
1421
private _isInitialized = false;
@@ -57,15 +64,45 @@ class AppStore {
5764
runInAction(() => {
5865
this._dataFetchMode = value;
5966
});
67+
68+
(async () => {
69+
try {
70+
await Provider.fetchProviders();
71+
await Wallet.fetchBalances();
72+
RNRestart.restart();
73+
} catch {
74+
if (value === DataFetchSource.Backend) {
75+
Alert.alert(
76+
'Haqq Wallet Backend Shutdown',
77+
'The backend service for Haqq Wallet is no longer maintained. Please switch to RPC mode.',
78+
[
79+
{
80+
text: 'Switch to RPC',
81+
onPress: () => {
82+
this.dataFetchMode = DataFetchSource.Rpc;
83+
},
84+
},
85+
],
86+
);
87+
} else {
88+
RNRestart.restart();
89+
}
90+
}
91+
})();
6092
}
6193

6294
get isRpcOnly() {
6395
return this.dataFetchMode === DataFetchSource.Rpc;
6496
}
6597

98+
get isHydrated() {
99+
return isHydrated(this);
100+
}
101+
66102
get isInitialized() {
67-
return isHydrated(this) && this._isInitialized;
103+
return this.isHydrated && this._isInitialized;
68104
}
105+
69106
set isInitialized(value: boolean) {
70107
this._isInitialized = value;
71108
}

src/models/provider/provider-config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ProviderConfigStore {
3333
this._data[Provider.selectedProvider.ethChainId] = config;
3434
});
3535
}
36-
this.lazyLoadOtherConfig();
36+
await this.lazyLoadOtherConfig();
3737
return Promise.resolve();
3838
} catch (error) {
3939
Logger.captureException(error, 'ProviderConfigStore:init');

src/screens/HomeStack/home.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {awaitForEventDone} from '@app/helpers/await-for-event-done';
2424
import {getProviderStorage} from '@app/helpers/sss';
2525
import {useTypedNavigation} from '@app/hooks';
2626
import {useEffectAsync} from '@app/hooks/use-effect-async';
27+
import {Provider} from '@app/models/provider';
2728
import {VariablesBool} from '@app/models/variables-bool';
2829
import {Wallet} from '@app/models/wallet';
2930
import {
@@ -227,7 +228,7 @@ export const HomeScreen = observer(() => {
227228
};
228229
}, []);
229230

230-
if (!isAppUnlocked) {
231+
if (!isAppUnlocked || !Provider.selectedProvider) {
231232
return <Loading />;
232233
}
233234

src/screens/settings-developer-tools.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,6 @@ TRON:\n${AddressUtils.toTron(watchOnlyAddress)}`,
309309
}
310310
setDataFetchSourceSwitching(true);
311311
AppStore.dataFetchMode = tab;
312-
await Provider.fetchProviders();
313-
await Wallet.fetchBalances();
314-
RNRestart.restart();
315312
}}>
316313
{DATA_FETCH_SOURCES.map(tab => (
317314
<TopTabNavigator.Tab name={tab} title={tab} component={null} />

src/types.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,7 @@ export interface ILayoutWidget extends IWidgetBase {
13101310
component: 'Layout';
13111311
direction: 'horizontal' | 'vertical';
13121312
child: IWidget[];
1313+
id: string;
13131314
}
13141315

13151316
export interface IAdWidget extends IWidgetBase, Banner {
@@ -1343,7 +1344,7 @@ export interface ISwapWidget extends IWidgetBase {
13431344
component: 'Swap';
13441345
}
13451346

1346-
export type IWidget =
1347+
export type IWidget = {id: string} & (
13471348
| ITransactionsWidget
13481349
| ITransactionsShortWidget
13491350
| IRafflesWidget
@@ -1354,7 +1355,8 @@ export type IWidget =
13541355
| IBannerWidget
13551356
| ITokensWidget
13561357
| INftWidget
1357-
| ISwapWidget;
1358+
| ISwapWidget
1359+
);
13581360

13591361
export interface MarkupResponse {
13601362
blocks: ILayoutWidget;
@@ -1645,6 +1647,8 @@ export enum DeeplinkUrlKey {
16451647
enableDeveloperMode = 'enableDeveloperMode',
16461648
enableNetworkLogger = 'enableNetworkLogger',
16471649
export = 'export',
1650+
noBackend = 'noBackend',
1651+
useRpc = 'useRpc',
16481652
}
16491653

16501654
export type Eventable = Required<{

src/widgets/index.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,21 @@ import {TransactionsWidgetWrapper} from '@app/widgets/transactions-widget';
2222

2323
const MOCK_MARKUP: IWidget[] = [
2424
{
25+
id: 'home-widget',
2526
component: 'Layout',
2627
direction: 'vertical',
2728
child: [
28-
{
29-
component: 'TransactionsShort',
30-
},
3129
{
3230
component: 'TokenList',
31+
id: 'tokens-widget',
3332
},
3433
{
3534
component: 'Transactions',
35+
id: 'transactions-widget',
3636
},
3737
{
3838
component: 'Governance',
39+
id: 'governance-widget',
3940
},
4041
],
4142
},

0 commit comments

Comments
 (0)