mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
added dev options to prod builds
This commit is contained in:
parent
fd1e303403
commit
031c0c8772
3 changed files with 326 additions and 11 deletions
|
|
@ -205,12 +205,26 @@ const SettingsScreen: React.FC = () => {
|
||||||
|
|
||||||
// States for dynamic content
|
// States for dynamic content
|
||||||
const [mdblistKeySet, setMdblistKeySet] = useState<boolean>(false);
|
const [mdblistKeySet, setMdblistKeySet] = useState<boolean>(false);
|
||||||
|
const [developerModeEnabled, setDeveloperModeEnabled] = useState<boolean>(false);
|
||||||
const [totalDownloads, setTotalDownloads] = useState<number>(0);
|
const [totalDownloads, setTotalDownloads] = useState<number>(0);
|
||||||
const [displayDownloads, setDisplayDownloads] = useState<number | null>(null);
|
const [displayDownloads, setDisplayDownloads] = useState<number | null>(null);
|
||||||
|
|
||||||
// Use Realtime Config Hook
|
// Use Realtime Config Hook
|
||||||
const settingsConfig = useRealtimeConfig();
|
const settingsConfig = useRealtimeConfig();
|
||||||
|
|
||||||
|
// Load developer mode state
|
||||||
|
useEffect(() => {
|
||||||
|
const loadDevModeState = async () => {
|
||||||
|
try {
|
||||||
|
const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled');
|
||||||
|
setDeveloperModeEnabled(devModeEnabled === 'true');
|
||||||
|
} catch (error) {
|
||||||
|
if (__DEV__) console.error('Failed to load developer mode state:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadDevModeState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Scroll to top ref and handler
|
// Scroll to top ref and handler
|
||||||
const mobileScrollViewRef = useRef<ScrollView>(null);
|
const mobileScrollViewRef = useRef<ScrollView>(null);
|
||||||
const tabletScrollViewRef = useRef<ScrollView>(null);
|
const tabletScrollViewRef = useRef<ScrollView>(null);
|
||||||
|
|
@ -236,6 +250,10 @@ const SettingsScreen: React.FC = () => {
|
||||||
const mdblistKey = await mmkvStorage.getItem('mdblist_api_key');
|
const mdblistKey = await mmkvStorage.getItem('mdblist_api_key');
|
||||||
setMdblistKeySet(!!mdblistKey);
|
setMdblistKeySet(!!mdblistKey);
|
||||||
|
|
||||||
|
// Check developer mode status
|
||||||
|
const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled');
|
||||||
|
setDeveloperModeEnabled(devModeEnabled === 'true');
|
||||||
|
|
||||||
// Load GitHub total downloads
|
// Load GitHub total downloads
|
||||||
const downloads = await fetchTotalDownloads();
|
const downloads = await fetchTotalDownloads();
|
||||||
if (downloads !== null) {
|
if (downloads !== null) {
|
||||||
|
|
@ -339,7 +357,7 @@ const SettingsScreen: React.FC = () => {
|
||||||
// Filter categories based on conditions
|
// Filter categories based on conditions
|
||||||
const visibleCategories = SETTINGS_CATEGORIES.filter(category => {
|
const visibleCategories = SETTINGS_CATEGORIES.filter(category => {
|
||||||
if (settingsConfig?.categories?.[category.id]?.visible === false) return false;
|
if (settingsConfig?.categories?.[category.id]?.visible === false) return false;
|
||||||
if (category.id === 'developer' && !__DEV__) return false;
|
if (category.id === 'developer' && !__DEV__ && !developerModeEnabled) return false;
|
||||||
if (category.id === 'cache' && !mdblistKeySet) return false;
|
if (category.id === 'cache' && !mdblistKeySet) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
@ -380,7 +398,7 @@ const SettingsScreen: React.FC = () => {
|
||||||
return <AboutSettingsContent isTablet={isTablet} displayDownloads={displayDownloads} />;
|
return <AboutSettingsContent isTablet={isTablet} displayDownloads={displayDownloads} />;
|
||||||
|
|
||||||
case 'developer':
|
case 'developer':
|
||||||
return __DEV__ ? (
|
return (__DEV__ || developerModeEnabled) ? (
|
||||||
<SettingsCard title={t('settings.sections.testing')} isTablet={isTablet}>
|
<SettingsCard title={t('settings.sections.testing')} isTablet={isTablet}>
|
||||||
<SettingItem
|
<SettingItem
|
||||||
title={t('settings.items.test_onboarding')}
|
title={t('settings.items.test_onboarding')}
|
||||||
|
|
@ -721,8 +739,8 @@ const SettingsScreen: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
|
|
||||||
{/* Developer - only in DEV mode */}
|
{/* Developer - visible in DEV mode or when developer mode is enabled */}
|
||||||
{__DEV__ && (
|
{(__DEV__ || developerModeEnabled) && (
|
||||||
<SettingsCard title={t('settings.sections.testing')}>
|
<SettingsCard title={t('settings.sections.testing')}>
|
||||||
<SettingItem
|
<SettingItem
|
||||||
title={t('settings.items.developer_tools')}
|
title={t('settings.items.developer_tools')}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { View, Text, StyleSheet, ScrollView, StatusBar, TouchableOpacity, Platform, Linking, Dimensions } from 'react-native';
|
import { View, Text, StyleSheet, ScrollView, StatusBar, TouchableOpacity, Platform, Linking, Dimensions, Alert, TextInput, Modal, KeyboardAvoidingView } from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { NavigationProp } from '@react-navigation/native';
|
import { NavigationProp } from '@react-navigation/native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
@ -14,12 +14,15 @@ import { getDisplayedAppVersion } from '../../utils/version';
|
||||||
import ScreenHeader from '../../components/common/ScreenHeader';
|
import ScreenHeader from '../../components/common/ScreenHeader';
|
||||||
import { SettingsCard, SettingItem, ChevronRight } from './SettingsComponents';
|
import { SettingsCard, SettingItem, ChevronRight } from './SettingsComponents';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { mmkvStorage } from '../../services/mmkvStorage';
|
||||||
|
import CustomAlert from '../../components/CustomAlert';
|
||||||
|
|
||||||
const { width } = Dimensions.get('window');
|
const { width } = Dimensions.get('window');
|
||||||
|
|
||||||
interface AboutSettingsContentProps {
|
interface AboutSettingsContentProps {
|
||||||
isTablet?: boolean;
|
isTablet?: boolean;
|
||||||
displayDownloads?: number | null;
|
displayDownloads?: number | null;
|
||||||
|
onDevModeChange?: (enabled: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -28,17 +31,60 @@ interface AboutSettingsContentProps {
|
||||||
*/
|
*/
|
||||||
export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
|
export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
|
||||||
isTablet = false,
|
isTablet = false,
|
||||||
displayDownloads: externalDisplayDownloads
|
displayDownloads: externalDisplayDownloads,
|
||||||
|
onDevModeChange
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||||
const { currentTheme } = useTheme();
|
const { currentTheme } = useTheme();
|
||||||
|
|
||||||
const [internalDisplayDownloads, setInternalDisplayDownloads] = useState<number | null>(null);
|
const [internalDisplayDownloads, setInternalDisplayDownloads] = useState<number | null>(null);
|
||||||
|
const [developerModeEnabled, setDeveloperModeEnabled] = useState(false);
|
||||||
|
const [tapCount, setTapCount] = useState(0);
|
||||||
|
const tapTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
// Developer code entry modal state
|
||||||
|
const [showCodeModal, setShowCodeModal] = useState(false);
|
||||||
|
const [codeInput, setCodeInput] = useState('');
|
||||||
|
|
||||||
|
// CustomAlert state
|
||||||
|
const [alertState, setAlertState] = useState<{
|
||||||
|
visible: boolean;
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
actions: Array<{ label: string; onPress: () => void; style?: object }>;
|
||||||
|
}>({
|
||||||
|
visible: false,
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
actions: [{ label: 'OK', onPress: () => { } }]
|
||||||
|
});
|
||||||
|
|
||||||
|
const showAlert = (title: string, message: string, actions?: Array<{ label: string; onPress: () => void; style?: object }>) => {
|
||||||
|
setAlertState({
|
||||||
|
visible: true,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
actions: actions || [{ label: 'OK', onPress: () => { } }]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Use external downloads if provided (for tablet inline use), otherwise load internally
|
// Use external downloads if provided (for tablet inline use), otherwise load internally
|
||||||
const displayDownloads = externalDisplayDownloads ?? internalDisplayDownloads;
|
const displayDownloads = externalDisplayDownloads ?? internalDisplayDownloads;
|
||||||
|
|
||||||
|
// Load developer mode state on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const loadDevModeState = async () => {
|
||||||
|
try {
|
||||||
|
const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled');
|
||||||
|
setDeveloperModeEnabled(devModeEnabled === 'true');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load developer mode state:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadDevModeState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only load downloads internally if not provided externally
|
// Only load downloads internally if not provided externally
|
||||||
if (externalDisplayDownloads === undefined) {
|
if (externalDisplayDownloads === undefined) {
|
||||||
|
|
@ -52,6 +98,88 @@ export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
|
||||||
}
|
}
|
||||||
}, [externalDisplayDownloads]);
|
}, [externalDisplayDownloads]);
|
||||||
|
|
||||||
|
const handleVersionTap = () => {
|
||||||
|
// If already in developer mode, do nothing on tap
|
||||||
|
if (developerModeEnabled) return;
|
||||||
|
|
||||||
|
// Clear previous timeout
|
||||||
|
if (tapTimeoutRef.current) {
|
||||||
|
clearTimeout(tapTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTapCount = tapCount + 1;
|
||||||
|
setTapCount(newTapCount);
|
||||||
|
|
||||||
|
// Reset tap count after 2 seconds of no tapping
|
||||||
|
tapTimeoutRef.current = setTimeout(() => {
|
||||||
|
setTapCount(0);
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
// Trigger developer mode unlock after 5 taps
|
||||||
|
if (newTapCount >= 5) {
|
||||||
|
setTapCount(0);
|
||||||
|
promptForDeveloperCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const promptForDeveloperCode = () => {
|
||||||
|
setCodeInput('');
|
||||||
|
setShowCodeModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyDeveloperCode = async () => {
|
||||||
|
setShowCodeModal(false);
|
||||||
|
const expectedCode = process.env.EXPO_PUBLIC_DEV_MODE_CODE || '815787';
|
||||||
|
if (codeInput === expectedCode) {
|
||||||
|
try {
|
||||||
|
await mmkvStorage.setItem('developer_mode_enabled', 'true');
|
||||||
|
setDeveloperModeEnabled(true);
|
||||||
|
onDevModeChange?.(true);
|
||||||
|
showAlert(
|
||||||
|
t('settings.developer_mode.enabled_title', 'Developer Mode Enabled'),
|
||||||
|
t('settings.developer_mode.enabled_message', 'Developer tools are now available in Settings.')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save developer mode state:', error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showAlert(
|
||||||
|
t('settings.developer_mode.invalid_code_title', 'Invalid Code'),
|
||||||
|
t('settings.developer_mode.invalid_code_message', 'The code you entered is incorrect.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setCodeInput('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDisableDeveloperMode = () => {
|
||||||
|
showAlert(
|
||||||
|
t('settings.developer_mode.disable_title', 'Disable Developer Mode'),
|
||||||
|
t('settings.developer_mode.disable_message', 'Are you sure you want to disable developer mode?'),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: t('common.cancel', 'Cancel'),
|
||||||
|
onPress: () => { },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('common.disable', 'Disable'),
|
||||||
|
onPress: async () => {
|
||||||
|
try {
|
||||||
|
await mmkvStorage.setItem('developer_mode_enabled', 'false');
|
||||||
|
setDeveloperModeEnabled(false);
|
||||||
|
onDevModeChange?.(false);
|
||||||
|
showAlert(
|
||||||
|
t('settings.developer_mode.disabled_title', 'Developer Mode Disabled'),
|
||||||
|
t('settings.developer_mode.disabled_message', 'Developer tools are now hidden.')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save developer mode state:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsCard title={t('settings.sections.information')} isTablet={isTablet}>
|
<SettingsCard title={t('settings.sections.information')} isTablet={isTablet}>
|
||||||
|
|
@ -80,6 +208,7 @@ export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
|
||||||
title={t('settings.items.version')}
|
title={t('settings.items.version')}
|
||||||
description={getDisplayedAppVersion()}
|
description={getDisplayedAppVersion()}
|
||||||
icon="info"
|
icon="info"
|
||||||
|
onPress={handleVersionTap}
|
||||||
isTablet={isTablet}
|
isTablet={isTablet}
|
||||||
/>
|
/>
|
||||||
<SettingItem
|
<SettingItem
|
||||||
|
|
@ -88,10 +217,90 @@ export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
|
||||||
icon="users"
|
icon="users"
|
||||||
renderControl={() => <ChevronRight />}
|
renderControl={() => <ChevronRight />}
|
||||||
onPress={() => navigation.navigate('Contributors')}
|
onPress={() => navigation.navigate('Contributors')}
|
||||||
isLast
|
isLast={!developerModeEnabled}
|
||||||
isTablet={isTablet}
|
isTablet={isTablet}
|
||||||
/>
|
/>
|
||||||
|
{developerModeEnabled && (
|
||||||
|
<SettingItem
|
||||||
|
title={t('settings.developer_mode.title', 'Developer Mode')}
|
||||||
|
description={t('settings.developer_mode.enabled_desc', 'Tap to disable developer mode')}
|
||||||
|
icon="code"
|
||||||
|
onPress={handleDisableDeveloperMode}
|
||||||
|
renderControl={() => <ChevronRight />}
|
||||||
|
isLast
|
||||||
|
isTablet={isTablet}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
|
|
||||||
|
{/* Developer Code Entry Modal */}
|
||||||
|
<Modal
|
||||||
|
visible={showCodeModal}
|
||||||
|
transparent
|
||||||
|
animationType="fade"
|
||||||
|
onRequestClose={() => setShowCodeModal(false)}
|
||||||
|
statusBarTranslucent
|
||||||
|
>
|
||||||
|
<KeyboardAvoidingView
|
||||||
|
style={modalStyles.overlay}
|
||||||
|
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||||
|
>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={modalStyles.backdrop}
|
||||||
|
activeOpacity={1}
|
||||||
|
onPress={() => setShowCodeModal(false)}
|
||||||
|
/>
|
||||||
|
<View style={modalStyles.container}>
|
||||||
|
<Text style={modalStyles.title}>
|
||||||
|
{t('settings.developer_mode.enter_code_title', 'Enable Developer Mode')}
|
||||||
|
</Text>
|
||||||
|
<Text style={modalStyles.message}>
|
||||||
|
{t('settings.developer_mode.enter_code_message', 'Enter the developer code to enable developer mode:')}
|
||||||
|
</Text>
|
||||||
|
<TextInput
|
||||||
|
style={modalStyles.input}
|
||||||
|
value={codeInput}
|
||||||
|
onChangeText={setCodeInput}
|
||||||
|
placeholder="Enter code"
|
||||||
|
placeholderTextColor="#888"
|
||||||
|
secureTextEntry
|
||||||
|
keyboardType="number-pad"
|
||||||
|
autoFocus
|
||||||
|
maxLength={10}
|
||||||
|
/>
|
||||||
|
<View style={modalStyles.buttonRow}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={modalStyles.cancelButton}
|
||||||
|
onPress={() => {
|
||||||
|
setShowCodeModal(false);
|
||||||
|
setCodeInput('');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={modalStyles.cancelButtonText}>
|
||||||
|
{t('common.cancel', 'Cancel')}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[modalStyles.confirmButton, { backgroundColor: currentTheme.colors.primary }]}
|
||||||
|
onPress={verifyDeveloperCode}
|
||||||
|
>
|
||||||
|
<Text style={modalStyles.confirmButtonText}>
|
||||||
|
{t('common.confirm', 'Confirm')}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* Custom Alert */}
|
||||||
|
<CustomAlert
|
||||||
|
visible={alertState.visible}
|
||||||
|
title={alertState.title}
|
||||||
|
message={alertState.message}
|
||||||
|
actions={alertState.actions}
|
||||||
|
onClose={() => setAlertState(prev => ({ ...prev, visible: false }))}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -308,4 +517,78 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Styles for the developer code entry modal
|
||||||
|
const modalStyles = StyleSheet.create({
|
||||||
|
overlay: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
||||||
|
},
|
||||||
|
backdrop: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
width: '85%',
|
||||||
|
maxWidth: 400,
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 24,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: '700',
|
||||||
|
marginBottom: 8,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
color: '#AAAAAA',
|
||||||
|
fontSize: 15,
|
||||||
|
marginBottom: 20,
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 14,
|
||||||
|
fontSize: 18,
|
||||||
|
color: '#FFFFFF',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 20,
|
||||||
|
letterSpacing: 4,
|
||||||
|
},
|
||||||
|
buttonRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
cancelButton: {
|
||||||
|
flex: 1,
|
||||||
|
paddingVertical: 14,
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
cancelButtonText: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
confirmButton: {
|
||||||
|
flex: 1,
|
||||||
|
paddingVertical: 14,
|
||||||
|
borderRadius: 12,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
confirmButtonText: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default AboutSettingsScreen;
|
export default AboutSettingsScreen;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { View, StyleSheet, ScrollView, StatusBar } from 'react-native';
|
import { View, StyleSheet, ScrollView, StatusBar } from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { NavigationProp } from '@react-navigation/native';
|
import { NavigationProp } from '@react-navigation/native';
|
||||||
|
|
@ -18,11 +18,25 @@ const DeveloperSettingsScreen: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
|
const [developerModeEnabled, setDeveloperModeEnabled] = useState(__DEV__);
|
||||||
const [alertVisible, setAlertVisible] = useState(false);
|
const [alertVisible, setAlertVisible] = useState(false);
|
||||||
const [alertTitle, setAlertTitle] = useState('');
|
const [alertTitle, setAlertTitle] = useState('');
|
||||||
const [alertMessage, setAlertMessage] = useState('');
|
const [alertMessage, setAlertMessage] = useState('');
|
||||||
const [alertActions, setAlertActions] = useState<Array<{ label: string; onPress: () => void }>>([]);
|
const [alertActions, setAlertActions] = useState<Array<{ label: string; onPress: () => void }>>([]);
|
||||||
|
|
||||||
|
// Load developer mode state on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const loadDevModeState = async () => {
|
||||||
|
try {
|
||||||
|
const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled');
|
||||||
|
setDeveloperModeEnabled(__DEV__ || devModeEnabled === 'true');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load developer mode state:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadDevModeState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const openAlert = (
|
const openAlert = (
|
||||||
title: string,
|
title: string,
|
||||||
message: string,
|
message: string,
|
||||||
|
|
@ -78,8 +92,8 @@ const DeveloperSettingsScreen: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only show in development mode
|
// Only show if developer mode is enabled (via __DEV__ or manually unlocked)
|
||||||
if (!__DEV__) {
|
if (!developerModeEnabled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue