Skip to content

Commit b8deccf

Browse files
Preparations for debug menu
1 parent 74c3a3d commit b8deccf

File tree

7 files changed

+105
-2
lines changed

7 files changed

+105
-2
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useCallback, useRef, useState } from 'react';
2+
3+
const useRequireMultiplePresses = (
4+
onPress: () => void,
5+
requiredPresses = 3,
6+
timeout = 500,
7+
) => {
8+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
9+
const [_, setPresses] = useState(0);
10+
const timeoutRef = useRef<NodeJS.Timeout>();
11+
12+
return useCallback(() => {
13+
setPresses(prevPresses => {
14+
if (prevPresses === 0) {
15+
timeoutRef.current = setTimeout(() => {
16+
setPresses(0);
17+
}, timeout);
18+
}
19+
20+
if (prevPresses + 1 === requiredPresses) {
21+
clearTimeout(timeoutRef.current);
22+
onPress();
23+
return 0;
24+
}
25+
26+
return prevPresses + 1;
27+
});
28+
}, [onPress, requiredPresses, timeout]);
29+
};
30+
31+
export default useRequireMultiplePresses;

src/hooks/useSettings.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { EqualityFn } from 'react-redux';
2+
3+
import type { SettingsState } from '@/types/settings';
4+
5+
import { useAppSelector } from '@/store';
6+
7+
const useSettings = <T>(
8+
selector: (state: SettingsState) => T,
9+
equalityFn?: EqualityFn<T>,
10+
): T => useAppSelector(state => selector(state.settings), equalityFn);
11+
12+
export default useSettings;

src/slices/settings.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type {
1818
UpdateDtuHostnameAction,
1919
UpdateDtuSerialNumberAction,
2020
SetLanguageAction,
21-
EnableAppUpdatesAction,
21+
EnableAppUpdatesAction, DebugEnabledAction,
2222
} from '@/types/settings';
2323

2424
const initialState: SettingsState = {
@@ -28,6 +28,7 @@ const initialState: SettingsState = {
2828
selectedDtuConfig: null,
2929
databaseConfigs: [],
3030
enableAppUpdates: null,
31+
debugEnabled: false,
3132
};
3233

3334
const log = logger.createLogger();
@@ -209,6 +210,9 @@ const settingsSlice = createSlice({
209210
setEnableAppUpdates: (state, action: EnableAppUpdatesAction) => {
210211
state.enableAppUpdates = action.payload.enable;
211212
},
213+
setDebugEnabled: (state, action: DebugEnabledAction) => {
214+
state.debugEnabled = action.payload.debugEnabled;
215+
},
212216
},
213217
});
214218

@@ -231,6 +235,7 @@ export const {
231235
updateDatabaseConfig,
232236
updateDTUDatabaseUuid,
233237
setEnableAppUpdates,
238+
setDebugEnabled,
234239
} = settingsSlice.actions;
235240

236241
export const { reducer: SettingsReducer } = settingsSlice;

src/types/settings.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface SettingsState {
2929
appTheme: 'light' | 'dark' | 'system';
3030
language: 'en' | 'de';
3131
enableAppUpdates: boolean | null;
32+
debugEnabled: boolean;
3233

3334
// opendtu
3435
dtuConfigs: OpenDTUConfig[];
@@ -105,3 +106,7 @@ export type UpdateDTUDatabaseUuidAction = PayloadAction<{
105106
export type EnableAppUpdatesAction = PayloadAction<{
106107
enable: boolean;
107108
}>;
109+
110+
export type DebugEnabledAction = PayloadAction<{
111+
debugEnabled: boolean;
112+
}>;

src/views/navigation/NavigationStack.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import SelectDatabaseScreen from '@/views/navigation/screens/SelectDatabaseScree
2323
import SetupAddOpenDTUScreen from '@/views/navigation/screens/SetupAddOpenDTUScreen';
2424
import SetupAuthenticateOpenDTUInstanceScreen from '@/views/navigation/screens/SetupAuthenticateOpenDTUInstanceScreen';
2525
import SetupOpenDTUCompleteScreen from '@/views/navigation/screens/SetupOpenDTUCompleteScreen';
26+
import DebugScreen from '@/views/navigation/screens/DebugScreen';
2627

2728
export type PropsWithNavigation = {
2829
navigation: NavigationProp<ParamListBase>;
@@ -119,6 +120,11 @@ const NavigationStack: FC = () => {
119120
component={LicensesScreen}
120121
options={{ headerBackVisible: true }}
121122
/>
123+
<Stack.Screen
124+
name="DebugScreen"
125+
component={DebugScreen}
126+
options={{ headerBackVisible: true }}
127+
/>
122128
</Stack.Navigator>
123129
);
124130
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { FC } from 'react';
2+
import { Appbar } from 'react-native-paper';
3+
4+
import type { PropsWithNavigation } from '@/views/navigation/NavigationStack';
5+
6+
const DebugScreen: FC<PropsWithNavigation> = () => {
7+
return (
8+
<>
9+
<Appbar.Header>
10+
<Appbar.Content title="Debug" />
11+
</Appbar.Header>
12+
</>
13+
);
14+
};
15+
16+
export default DebugScreen;

src/views/navigation/tabs/MainSettingsTab.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
import type { NavigationProp, ParamListBase } from '@react-navigation/native';
22
import { useNavigation } from '@react-navigation/native';
3+
import packageJson from '@root/package.json';
34

45
import type { FC } from 'react';
56
import { useMemo, useCallback, useState } from 'react';
67
import { useTranslation } from 'react-i18next';
78
import { ScrollView } from 'react-native';
89
import { Box } from 'react-native-flex-layout';
9-
import { Badge, List, useTheme } from 'react-native-paper';
10+
import { Badge, List, Text, useTheme } from 'react-native-paper';
11+
import { useDispatch } from 'react-redux';
12+
13+
import { setDebugEnabled } from '@/slices/settings';
1014

1115
import ChangeLanguageModal from '@/components/modals/ChangeLanguageModal';
1216
import ChangeThemeModal from '@/components/modals/ChangeThemeModal';
1317

1418
import useDtuState from '@/hooks/useDtuState';
1519
import useHasNewAppVersion from '@/hooks/useHasNewAppVersion';
1620
import useIsConnected from '@/hooks/useIsConnected';
21+
import useRequireMultiplePresses from '@/hooks/useRequireMultiplePresses';
22+
import useSettings from '@/hooks/useSettings';
1723

1824
import { StyledSafeAreaView } from '@/style';
1925

2026
const MainSettingsTab: FC = () => {
2127
const navigation = useNavigation() as NavigationProp<ParamListBase>;
2228
const { t } = useTranslation();
29+
const dispatch = useDispatch();
2330

2431
const theme = useTheme();
2532

@@ -40,6 +47,8 @@ const MainSettingsTab: FC = () => {
4047
usedForIndicatorOnly: true,
4148
});
4249

50+
const showDebugScreen = useSettings(state => state?.debugEnabled);
51+
4352
const hasSystemInformation = !!useDtuState(state => !!state?.systemStatus);
4453
const hasNetworkInformation = !!useDtuState(state => !!state?.networkStatus);
4554
const hasNtpInformation = !!useDtuState(state => !!state?.ntpStatus);
@@ -86,6 +95,15 @@ const MainSettingsTab: FC = () => {
8695
navigation.navigate('MqttInformationScreen');
8796
}, [navigation]);
8897

98+
const handleDebugScreen = useCallback(() => {
99+
navigation.navigate('DebugScreen');
100+
}, [navigation]);
101+
102+
const enableDebugMode = useCallback(() => {
103+
dispatch(setDebugEnabled({ debugEnabled: true }));
104+
}, [dispatch]);
105+
const handleUnlockDebug = useRequireMultiplePresses(enableDebugMode);
106+
89107
return (
90108
<StyledSafeAreaView theme={theme}>
91109
<Box style={{ width: '100%', flex: 1 }}>
@@ -158,7 +176,17 @@ const MainSettingsTab: FC = () => {
158176
left={props => <List.Icon {...props} icon="file-document" />}
159177
onPress={handleLicenses}
160178
/>
179+
{showDebugScreen ? (
180+
<List.Item
181+
title={t('settings.debug')}
182+
left={props => <List.Icon {...props} icon="bug" />}
183+
onPress={handleDebugScreen}
184+
/>
185+
) : null}
161186
</List.Section>
187+
<Text style={{ textAlign: 'center' }} onPress={handleUnlockDebug}>
188+
Version {packageJson.version}
189+
</Text>
162190
</ScrollView>
163191
</Box>
164192
<ChangeThemeModal

0 commit comments

Comments
 (0)