Skip to content

Commit 52d490c

Browse files
committed
added periodic monitor of blox status
1 parent 1bf3d0a commit 52d490c

16 files changed

Lines changed: 333 additions & 5 deletions

File tree

apps/box/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
xmlns:tools="http://schemas.android.com/tools">
33

44
<uses-permission android:name="android.permission.INTERNET" />
5+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
56
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
67
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
78
<uses-permission android:name="android.permission.BLUETOOTH" tools:remove="android:maxSdkVersion" />

apps/box/ios/Box/AppDelegate.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import UIKit
22
import React
33
import React_RCTAppDelegate
44
import ReactAppDependencyProvider
5+
import TSBackgroundFetch
56

67
@main
78
class AppDelegate: RCTAppDelegate {
@@ -17,6 +18,9 @@ class AppDelegate: RCTAppDelegate {
1718
// They will be passed down to the ViewController used by React Native.
1819
self.initialProps = [:]
1920

21+
// Initialize background fetch
22+
TSBackgroundFetch.sharedInstance().didFinishLaunching()
23+
2024
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
2125
}
2226

apps/box/ios/Box/Info.plist

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@
101101
<string>UIInterfaceOrientationLandscapeLeft</string>
102102
<string>UIInterfaceOrientationLandscapeRight</string>
103103
</array>
104+
<key>UIBackgroundModes</key>
105+
<array>
106+
<string>fetch</string>
107+
<string>processing</string>
108+
</array>
109+
<key>BGTaskSchedulerPermittedIdentifiers</key>
110+
<array>
111+
<string>com.transistorsoft.fetch</string>
112+
</array>
104113
<key>UIViewControllerBasedStatusBarAppearance</key>
105114
<false/>
106115
</dict>

apps/box/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
"whatwg-fetch": "*",
9191
"zustand": "*",
9292
"@web3modal/wagmi-react-native": "*",
93-
"react-native-randombytes": "*"
93+
"react-native-randombytes": "*",
94+
"react-native-background-fetch": "*"
9495
},
9596
"devDependencies": {
9697
"@babel/core": "*",

apps/box/src/app/App.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { copyToClipboard } from '../utils/clipboard';
3030
import { fula } from '@functionland/react-native-fula';
3131
import { AppKitProvider, AppKit } from '@reown/appkit-react-native';
3232
import { appKit } from '../config/appKitConfig';
33+
import { configureBackgroundBloxCheck } from '../services/backgroundBloxCheck';
3334

3435
if (Platform.OS === 'android') {
3536
if (UIManager.setLayoutAnimationEnabledExperimental) {
@@ -81,8 +82,15 @@ export const App = () => {
8182
const AppContent = () => {
8283
const appState = useRef(AppState.currentState);
8384
const debugMode = useSettingsStore((state) => state.debugMode);
85+
const bloxStatusCheckInterval = useSettingsStore((state) => state.bloxStatusCheckInterval);
8486
const { isDebugModeEnable } = useLogger();
8587

88+
useEffect(() => {
89+
if (bloxStatusCheckInterval !== undefined) {
90+
configureBackgroundBloxCheck(bloxStatusCheckInterval).catch(console.error);
91+
}
92+
}, [bloxStatusCheckInterval]);
93+
8694
useEffect(() => {
8795
if (!__DEV__) {
8896
console.log = () => null;

apps/box/src/components/SettingsList/SettingsMenu.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,22 @@ import { useNavigation } from '@react-navigation/native';
99
import { useSettingsStore, useColorMode } from '../../stores';
1010
import { CHAIN_DISPLAY_NAMES } from '../../contracts/config';
1111

12+
const INTERVAL_LABELS: Record<number, string> = {
13+
0: 'Disabled',
14+
480: 'Every 8 hours',
15+
1440: 'Every 24 hours',
16+
};
17+
1218
export const SettingsMenu = () => {
1319
const navigation =
1420
useNavigation<SettingsStackNavigationProps<Routes.Settings>>();
1521
const rootNavigation = useNavigation();
1622

1723
const mode = useColorMode();
1824
const selectedChain = useSettingsStore((state) => state.selectedChain);
25+
const bloxStatusCheckInterval = useSettingsStore(
26+
(state) => state.bloxStatusCheckInterval
27+
);
1928

2029
// Add app component gallery in development mode
2130
const appGallery = __DEV__
@@ -30,9 +39,9 @@ export const SettingsMenu = () => {
3039

3140
const menuItems = [
3241
{
33-
name: 'Connected dApps',
34-
detail: null,
35-
onPress: () => navigation.navigate(Routes.ConnectedDApps),
42+
name: 'Blox Status Monitor',
43+
detail: INTERVAL_LABELS[bloxStatusCheckInterval] ?? 'Disabled',
44+
onPress: () => navigation.navigate(Routes.BloxStatusMonitor),
3645
},
3746
{
3847
name: 'Mode',

apps/box/src/main.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ if (typeof fetch === 'undefined') {
1616

1717
AppRegistry.registerComponent('Box', () => App);
1818

19+
import BackgroundFetch from 'react-native-background-fetch';
20+
import { headlessBloxCheckTask } from './services/backgroundBloxCheck';
21+
BackgroundFetch.registerHeadlessTask(headlessBloxCheckTask);
22+
1923
LogBox.ignoreLogs([
2024
'Require cycle: ../../node_modules/',
2125
'InteractionManager has been deprecated',

apps/box/src/navigation/MainTabs.navigator.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
ModeScreen,
3636
ChainSelectionScreen,
3737
ConnectedDAppsScreen,
38+
BloxStatusMonitorScreen,
3839
} from '../screens/Settings';
3940
import { ComponentGalleryNavigator } from './ComponentGallery.navigator';
4041
import { GlobalBottomSheet } from '../components/GlobalBottomSheet';
@@ -319,6 +320,10 @@ const SettingsNavigator = () => {
319320
name={Routes.ConnectedDApps}
320321
component={ConnectedDAppsScreen}
321322
/>
323+
<SettingsStack.Screen
324+
name={Routes.BloxStatusMonitor}
325+
component={BloxStatusMonitorScreen}
326+
/>
322327
<SettingsStack.Screen name={Routes.Mode} component={ModeScreen} />
323328
<SettingsStack.Screen name={Routes.ChainSelection} component={ChainSelectionScreen} />
324329
<SettingsStack.Screen name={Routes.Pools} component={PoolsScreen} />

apps/box/src/navigation/navigationConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export enum Routes {
3939
// Settings Stack
4040
Settings = 'Settings',
4141
ConnectedDApps = 'ConnectedDApps',
42+
BloxStatusMonitor = 'BloxStatusMonitor',
4243
Mode = 'Mode',
4344
ChainSelection = 'ChainSelection',
4445
Pools = 'Pools',
@@ -93,6 +94,7 @@ export type SettingsStackParamList = {
9394
returnDeepLink?: string;
9495
accountId?: string;
9596
};
97+
[Routes.BloxStatusMonitor]: undefined;
9698
[Routes.Mode]: undefined;
9799
[Routes.ChainSelection]: undefined;
98100
[Routes.Pools]: undefined;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {
2+
FxBox,
3+
FxRadioButton,
4+
FxRadioButtonWithLabel,
5+
FxText,
6+
} from '@functionland/component-library';
7+
import React from 'react';
8+
import { SubHeaderText } from '../../components/Text';
9+
import { useSettingsStore } from '../../stores';
10+
11+
const INTERVAL_OPTIONS = [
12+
{ value: '0', label: 'Disabled' },
13+
{ value: '480', label: 'Every 8 hours' },
14+
{ value: '1440', label: 'Every 24 hours' },
15+
] as const;
16+
17+
export const BloxStatusMonitorScreen = () => {
18+
const bloxStatusCheckInterval = useSettingsStore(
19+
(state) => state.bloxStatusCheckInterval
20+
);
21+
const setBloxStatusCheckInterval = useSettingsStore(
22+
(state) => state.setBloxStatusCheckInterval
23+
);
24+
25+
return (
26+
<FxBox marginHorizontal="20">
27+
<SubHeaderText marginVertical="16">Blox Status Monitor</SubHeaderText>
28+
<FxText variant="bodySmallRegular" marginBottom="8">
29+
Check interval
30+
</FxText>
31+
<FxText variant="bodyXSRegular" color="content3" marginBottom="16">
32+
When enabled, the app periodically checks if your bloxes are reachable
33+
and notifies you of any that are disconnected — even when the app is
34+
closed.
35+
</FxText>
36+
<FxRadioButton.Group
37+
value={String(bloxStatusCheckInterval)}
38+
onValueChange={(val: string) =>
39+
setBloxStatusCheckInterval(Number(val))
40+
}
41+
>
42+
{INTERVAL_OPTIONS.map((opt) => (
43+
<FxBox key={opt.value} marginBottom="8">
44+
<FxRadioButtonWithLabel value={opt.value} label={opt.label} />
45+
</FxBox>
46+
))}
47+
</FxRadioButton.Group>
48+
<FxText variant="bodyXSRegular" color="content3" marginTop="16">
49+
Note: On iOS, the system controls exact timing and may delay background
50+
tasks based on battery state and usage patterns. Each blox check takes
51+
approximately 30 seconds.
52+
</FxText>
53+
</FxBox>
54+
);
55+
};

0 commit comments

Comments
 (0)