761 lines
36 KiB
JavaScript
761 lines
36 KiB
JavaScript
/*
|
||
Предрейсовые осмотры - мобильное приложение
|
||
Экран настроек приложения
|
||
*/
|
||
|
||
//---------------------
|
||
//Подключение библиотек
|
||
//---------------------
|
||
|
||
const React = require('react'); //React и хуки
|
||
const { ScrollView, View, Pressable } = require('react-native'); //Базовые компоненты
|
||
const AdaptiveView = require('../components/common/AdaptiveView'); //Адаптивный контейнер
|
||
const AppText = require('../components/common/AppText'); //Общий текст
|
||
const AppButton = require('../components/common/AppButton'); //Кнопка
|
||
const AppSwitch = require('../components/common/AppSwitch'); //Переключатель
|
||
const CopyButton = require('../components/common/CopyButton'); //Кнопка копирования
|
||
const InputDialog = require('../components/common/InputDialog'); //Диалог ввода
|
||
const AppHeader = require('../components/layout/AppHeader'); //Заголовок
|
||
const { useAppMessagingContext } = require('../components/layout/AppMessagingProvider'); //Контекст сообщений
|
||
const { useAppModeContext } = require('../components/layout/AppModeProvider'); //Контекст режима
|
||
const { useAppNavigationContext } = require('../components/layout/AppNavigationProvider'); //Контекст навигации
|
||
const { useHardwareScannerContext } = require('../components/layout/HardwareScannerProvider'); //Встроенный сканер
|
||
const { useAppLocalDbContext } = require('../components/layout/AppLocalDbProvider'); //Контекст локальной БД
|
||
const { useAppAuthContext } = require('../components/layout/AppAuthProvider'); //Контекст авторизации
|
||
const { AUTH_SETTINGS_KEYS, DEFAULT_SERVER_REQUEST_TIMEOUT } = require('../config/authConfig'); //Конфиг авторизации
|
||
const { DIALOG_BUTTON_TYPE, DIALOG_CANCEL_BUTTON, getConfirmButtonOptions } = require('../config/dialogButtons'); //Кнопки диалогов
|
||
const { getAppInfo, getModeLabel } = require('../utils/appInfo'); //Информация о приложении и режиме
|
||
const { validateServerUrlAllowEmpty, validateIdleTimeoutAllowEmpty, validateServerRequestTimeout } = require('../utils/validation'); //Валидация
|
||
const {
|
||
APP_ABOUT_TITLE,
|
||
SETTINGS_SERVER_SAVED_MESSAGE,
|
||
SETTINGS_RESET_SUCCESS_MESSAGE,
|
||
MENU_ITEM_ABOUT,
|
||
SCANNER_SETTING_LABEL,
|
||
SCANNER_PRIORITY_LABEL,
|
||
SCANNER_PRIORITY_CAMERA,
|
||
SCANNER_PRIORITY_HARDWARE
|
||
} = require('../config/messages'); //Сообщения
|
||
const styles = require('../styles/screens/SettingsScreen.styles'); //Стили экрана
|
||
|
||
//-----------
|
||
//Тело модуля
|
||
//-----------
|
||
|
||
function SettingsScreen() {
|
||
const { showInfo, showError, showSuccess } = useAppMessagingContext();
|
||
const { APP_MODE, mode, setNotConnected } = useAppModeContext();
|
||
const { goBack, canGoBack } = useAppNavigationContext();
|
||
const { getSetting, setSetting, clearSettings, vacuum, isDbReady } = useAppLocalDbContext();
|
||
const { session, isAuthenticated, getDeviceId, setLastSavedServerUrlFromSettings } = useAppAuthContext();
|
||
|
||
const [serverUrl, setServerUrl] = React.useState('');
|
||
const [hideServerUrl, setHideServerUrl] = React.useState(false);
|
||
const [idleTimeout, setIdleTimeout] = React.useState('');
|
||
const [deviceId, setDeviceId] = React.useState('');
|
||
const [isLoading, setIsLoading] = React.useState(false);
|
||
const [isServerUrlDialogVisible, setIsServerUrlDialogVisible] = React.useState(false);
|
||
const [isIdleTimeoutDialogVisible, setIsIdleTimeoutDialogVisible] = React.useState(false);
|
||
const [serverRequestTimeout, setServerRequestTimeout] = React.useState('');
|
||
const [isServerRequestTimeoutDialogVisible, setIsServerRequestTimeoutDialogVisible] = React.useState(false);
|
||
const [alwaysShowScanner, setAlwaysShowScanner] = React.useState(false);
|
||
const [mainScannerPriority, setMainScannerPriority] = React.useState('camera');
|
||
|
||
const { registerHandler, unregisterHandler, SCREENS: scannerScreens } = useHardwareScannerContext();
|
||
const serverUrlDialogRef = React.useRef(null);
|
||
const idleTimeoutDialogRef = React.useRef(null);
|
||
const serverRequestTimeoutDialogRef = React.useRef(null);
|
||
|
||
//Предотвращение повторной загрузки настроек
|
||
const settingsLoadedRef = React.useRef(false);
|
||
|
||
//Обработчик встроенного сканера: только при открытом диалоге ввода — замена значения в поле (данные как есть); иначе ничего не делать
|
||
const handleHardwareScan = React.useCallback(
|
||
barcode => {
|
||
if (isServerUrlDialogVisible && serverUrlDialogRef.current) {
|
||
serverUrlDialogRef.current.setValueFromScanner(barcode);
|
||
} else if (isIdleTimeoutDialogVisible && idleTimeoutDialogRef.current) {
|
||
idleTimeoutDialogRef.current.setValueFromScanner(barcode);
|
||
} else if (isServerRequestTimeoutDialogVisible && serverRequestTimeoutDialogRef.current) {
|
||
serverRequestTimeoutDialogRef.current.setValueFromScanner(barcode);
|
||
}
|
||
},
|
||
[isServerUrlDialogVisible, isIdleTimeoutDialogVisible, isServerRequestTimeoutDialogVisible]
|
||
);
|
||
|
||
const settingsScanHandlerRef = React.useRef(handleHardwareScan);
|
||
settingsScanHandlerRef.current = handleHardwareScan;
|
||
const stableSettingsScanHandler = React.useCallback(barcode => {
|
||
if (typeof settingsScanHandlerRef.current === 'function') settingsScanHandlerRef.current(barcode);
|
||
}, []);
|
||
|
||
//Регистрация обработчика встроенного сканера на экране настроек
|
||
React.useEffect(() => {
|
||
registerHandler(scannerScreens.SETTINGS, stableSettingsScanHandler);
|
||
return function cleanup() {
|
||
unregisterHandler(scannerScreens.SETTINGS);
|
||
};
|
||
}, [registerHandler, unregisterHandler, scannerScreens.SETTINGS, stableSettingsScanHandler]);
|
||
|
||
//Загрузка сохраненных настроек при готовности БД
|
||
React.useEffect(() => {
|
||
//Выходим, если БД не готова или уже загрузили настройки
|
||
if (!isDbReady || settingsLoadedRef.current) {
|
||
return;
|
||
}
|
||
|
||
settingsLoadedRef.current = true;
|
||
|
||
const loadSettings = async () => {
|
||
setIsLoading(true);
|
||
try {
|
||
const savedUrl = await getSetting(AUTH_SETTINGS_KEYS.SERVER_URL);
|
||
if (savedUrl) {
|
||
setServerUrl(savedUrl);
|
||
}
|
||
|
||
const savedHideServerUrl = await getSetting(AUTH_SETTINGS_KEYS.HIDE_SERVER_URL);
|
||
setHideServerUrl(savedHideServerUrl === 'true' || savedHideServerUrl === true);
|
||
|
||
const savedIdleTimeout = await getSetting(AUTH_SETTINGS_KEYS.IDLE_TIMEOUT);
|
||
setIdleTimeout(savedIdleTimeout != null ? String(savedIdleTimeout).trim() : '');
|
||
|
||
const savedServerRequestTimeout = await getSetting(AUTH_SETTINGS_KEYS.SERVER_REQUEST_TIMEOUT);
|
||
const serverRequestTimeoutValue = savedServerRequestTimeout != null ? String(savedServerRequestTimeout).trim() : '';
|
||
if (serverRequestTimeoutValue) {
|
||
setServerRequestTimeout(serverRequestTimeoutValue);
|
||
} else {
|
||
const defaultValue = String(DEFAULT_SERVER_REQUEST_TIMEOUT);
|
||
setServerRequestTimeout(defaultValue);
|
||
await setSetting(AUTH_SETTINGS_KEYS.SERVER_REQUEST_TIMEOUT, defaultValue);
|
||
}
|
||
|
||
//Получаем или генерируем идентификатор устройства
|
||
const currentDeviceId = await getDeviceId();
|
||
setDeviceId(currentDeviceId || '');
|
||
|
||
const savedAlwaysShowScanner = await getSetting(AUTH_SETTINGS_KEYS.ALWAYS_SHOW_SCANNER);
|
||
setAlwaysShowScanner(savedAlwaysShowScanner === 'true' || savedAlwaysShowScanner === true);
|
||
|
||
const savedPriority = await getSetting(AUTH_SETTINGS_KEYS.MAIN_SCANNER_PRIORITY);
|
||
setMainScannerPriority(savedPriority === 'hardware' ? 'hardware' : 'camera');
|
||
} catch (error) {
|
||
showError('Не удалось загрузить настройки');
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
loadSettings();
|
||
}, [isDbReady, getSetting, setSetting, showError, getDeviceId]);
|
||
|
||
//Доступность редактирования URL сервера (только в режиме "Не подключено")
|
||
const isServerUrlEditable = React.useMemo(
|
||
() => !isLoading && isDbReady && mode === APP_MODE.NOT_CONNECTED,
|
||
[isLoading, isDbReady, mode, APP_MODE.NOT_CONNECTED]
|
||
);
|
||
|
||
//Стиль поля URL сервера при нажатии
|
||
const getServerUrlFieldPressableStyle = React.useCallback(
|
||
({ pressed }) => [
|
||
styles.serverUrlField,
|
||
isServerUrlEditable ? (pressed ? styles.serverUrlFieldPressed : null) : styles.serverUrlFieldDisabled
|
||
],
|
||
[isServerUrlEditable]
|
||
);
|
||
|
||
//Стиль поля времени простоя при нажатии
|
||
const getIdleTimeoutFieldPressableStyle = React.useCallback(
|
||
({ pressed }) => [styles.serverUrlField, pressed && styles.serverUrlFieldPressed],
|
||
[]
|
||
);
|
||
|
||
//Стиль поля времени ожидания сервера при нажатии
|
||
const getServerRequestTimeoutFieldPressableStyle = React.useCallback(
|
||
({ pressed }) => [styles.serverUrlField, pressed && styles.serverUrlFieldPressed],
|
||
[]
|
||
);
|
||
|
||
//Открытие диалога ввода URL сервера (только в режиме "Не подключено")
|
||
const handleOpenServerUrlDialog = React.useCallback(() => {
|
||
if (!isServerUrlEditable) {
|
||
return;
|
||
}
|
||
setIsServerUrlDialogVisible(true);
|
||
}, [isServerUrlEditable]);
|
||
|
||
//Закрытие диалога ввода URL сервера
|
||
const handleCloseServerUrlDialog = React.useCallback(() => {
|
||
setIsServerUrlDialogVisible(false);
|
||
}, []);
|
||
|
||
//Сохранение настроек сервера
|
||
const handleSaveServerUrl = React.useCallback(
|
||
async url => {
|
||
setIsServerUrlDialogVisible(false);
|
||
setIsLoading(true);
|
||
|
||
const valueToSave = url != null ? String(url).trim() : '';
|
||
|
||
try {
|
||
const success = await setSetting(AUTH_SETTINGS_KEYS.SERVER_URL, valueToSave);
|
||
|
||
if (success) {
|
||
setServerUrl(valueToSave);
|
||
if (valueToSave) {
|
||
setLastSavedServerUrlFromSettings(valueToSave);
|
||
}
|
||
showSuccess(SETTINGS_SERVER_SAVED_MESSAGE);
|
||
} else {
|
||
showError('Не удалось сохранить настройки');
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось сохранить настройки');
|
||
}
|
||
|
||
setIsLoading(false);
|
||
},
|
||
[setSetting, showError, showSuccess, setLastSavedServerUrlFromSettings]
|
||
);
|
||
|
||
//Переключатель «Всегда отображать сканер на главном экране»
|
||
const handleToggleAlwaysShowScanner = React.useCallback(
|
||
async value => {
|
||
try {
|
||
const success = await setSetting(AUTH_SETTINGS_KEYS.ALWAYS_SHOW_SCANNER, value ? 'true' : 'false');
|
||
|
||
if (success) {
|
||
setAlwaysShowScanner(value);
|
||
showSuccess('Настройка сохранена');
|
||
} else {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
},
|
||
[setSetting, showSuccess, showError]
|
||
);
|
||
|
||
//Выбор приоритета на главном экране: камера или встроенный сканер
|
||
const handleSelectScannerPriority = React.useCallback(
|
||
async value => {
|
||
if (value !== 'camera' && value !== 'hardware') return;
|
||
try {
|
||
const success = await setSetting(AUTH_SETTINGS_KEYS.MAIN_SCANNER_PRIORITY, value);
|
||
if (success) {
|
||
setMainScannerPriority(value);
|
||
showSuccess('Настройка сохранена');
|
||
} else {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
},
|
||
[setSetting, showSuccess, showError]
|
||
);
|
||
|
||
//Выбор приоритета: камера
|
||
const handleSelectCameraPriority = React.useCallback(() => {
|
||
handleSelectScannerPriority('camera');
|
||
}, [handleSelectScannerPriority]);
|
||
|
||
//Выбор приоритета: встроенный сканер
|
||
const handleSelectHardwarePriority = React.useCallback(() => {
|
||
handleSelectScannerPriority('hardware');
|
||
}, [handleSelectScannerPriority]);
|
||
|
||
//Переключатель скрытия URL сервера в окне логина
|
||
const handleToggleHideServerUrl = React.useCallback(
|
||
async value => {
|
||
try {
|
||
const success = await setSetting(AUTH_SETTINGS_KEYS.HIDE_SERVER_URL, value ? 'true' : 'false');
|
||
|
||
if (success) {
|
||
setHideServerUrl(value);
|
||
showSuccess('Настройка сохранена');
|
||
} else {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
},
|
||
[setSetting, showSuccess, showError]
|
||
);
|
||
|
||
//Открытие диалога ввода времени простоя
|
||
const handleOpenIdleTimeoutDialog = React.useCallback(() => {
|
||
setIsIdleTimeoutDialogVisible(true);
|
||
}, []);
|
||
|
||
//Закрытие диалога ввода времени простоя
|
||
const handleCloseIdleTimeoutDialog = React.useCallback(() => {
|
||
setIsIdleTimeoutDialogVisible(false);
|
||
}, []);
|
||
|
||
//Открытие диалога ввода времени ожидания ответа от сервера
|
||
const handleOpenServerRequestTimeoutDialog = React.useCallback(() => {
|
||
setIsServerRequestTimeoutDialogVisible(true);
|
||
}, []);
|
||
|
||
//Закрытие диалога ввода времени ожидания ответа от сервера
|
||
const handleCloseServerRequestTimeoutDialog = React.useCallback(() => {
|
||
setIsServerRequestTimeoutDialogVisible(false);
|
||
}, []);
|
||
|
||
//Сохранение времени простоя
|
||
const handleSaveIdleTimeout = React.useCallback(
|
||
async value => {
|
||
setIsIdleTimeoutDialogVisible(false);
|
||
setIsLoading(true);
|
||
|
||
try {
|
||
const trimmedValue = value ? value.trim() : '';
|
||
const success = await setSetting(AUTH_SETTINGS_KEYS.IDLE_TIMEOUT, trimmedValue);
|
||
|
||
if (success) {
|
||
setIdleTimeout(trimmedValue);
|
||
showSuccess('Время простоя сохранено');
|
||
} else {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
|
||
setIsLoading(false);
|
||
},
|
||
[setSetting, showSuccess, showError]
|
||
);
|
||
|
||
//Сохранение времени ожидания ответа от сервера
|
||
const handleSaveServerRequestTimeout = React.useCallback(
|
||
async value => {
|
||
setIsServerRequestTimeoutDialogVisible(false);
|
||
setIsLoading(true);
|
||
|
||
try {
|
||
const trimmedValue = value != null ? String(value).trim() : '';
|
||
const success = await setSetting(AUTH_SETTINGS_KEYS.SERVER_REQUEST_TIMEOUT, trimmedValue);
|
||
|
||
if (success) {
|
||
setServerRequestTimeout(trimmedValue);
|
||
showSuccess('Время ожидания сервера сохранено');
|
||
} else {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось сохранить настройку');
|
||
}
|
||
|
||
setIsLoading(false);
|
||
},
|
||
[setSetting, showSuccess, showError]
|
||
);
|
||
|
||
//Выполнение очистки кэша
|
||
const performClearCache = React.useCallback(async () => {
|
||
showSuccess('Кэш успешно очищен');
|
||
}, [showSuccess]);
|
||
|
||
//Очистка кэша — диалог подтверждения, по подтверждению ничего не выполняется
|
||
const handleClearCache = React.useCallback(() => {
|
||
const confirmButton = getConfirmButtonOptions(DIALOG_BUTTON_TYPE.ERROR, 'Очистить', performClearCache);
|
||
|
||
showInfo('Очистить кэш приложения?', {
|
||
title: 'Подтверждение',
|
||
buttons: [DIALOG_CANCEL_BUTTON, confirmButton]
|
||
});
|
||
}, [showInfo, performClearCache]);
|
||
|
||
//Выполнение сброса настроек (для диалога подтверждения)
|
||
//Подключён (онлайн/офлайн): сбрасываем только непричастные к подключению настройки; не подключён: полный сброс
|
||
const performResetSettings = React.useCallback(async () => {
|
||
try {
|
||
if (mode === APP_MODE.NOT_CONNECTED) {
|
||
const success = await clearSettings();
|
||
if (success) {
|
||
const defaultServerTimeout = String(DEFAULT_SERVER_REQUEST_TIMEOUT);
|
||
await setSetting(AUTH_SETTINGS_KEYS.SERVER_REQUEST_TIMEOUT, defaultServerTimeout);
|
||
setServerUrl('');
|
||
setHideServerUrl(false);
|
||
setAlwaysShowScanner(false);
|
||
setMainScannerPriority('camera');
|
||
setIdleTimeout('');
|
||
setServerRequestTimeout(defaultServerTimeout);
|
||
setNotConnected();
|
||
showSuccess(SETTINGS_RESET_SUCCESS_MESSAGE);
|
||
} else {
|
||
showError('Не удалось сбросить настройки');
|
||
}
|
||
} else {
|
||
//Подключён (онлайн или офлайн): сбрасываем только время простоя и таймаут сервера
|
||
const defaultServerTimeout = String(DEFAULT_SERVER_REQUEST_TIMEOUT);
|
||
await setSetting(AUTH_SETTINGS_KEYS.IDLE_TIMEOUT, '');
|
||
await setSetting(AUTH_SETTINGS_KEYS.SERVER_REQUEST_TIMEOUT, defaultServerTimeout);
|
||
setIdleTimeout('');
|
||
setServerRequestTimeout(defaultServerTimeout);
|
||
showSuccess(SETTINGS_RESET_SUCCESS_MESSAGE);
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось сбросить настройки');
|
||
}
|
||
}, [
|
||
mode,
|
||
APP_MODE.NOT_CONNECTED,
|
||
setServerUrl,
|
||
setHideServerUrl,
|
||
setIdleTimeout,
|
||
setServerRequestTimeout,
|
||
setNotConnected,
|
||
clearSettings,
|
||
setSetting,
|
||
showSuccess,
|
||
showError
|
||
]);
|
||
|
||
//Сброс настроек: при подключении (онлайн/офлайн) — без настроек подключения; при отсутствии подключения — все настройки
|
||
const handleResetSettings = React.useCallback(() => {
|
||
const confirmButton = getConfirmButtonOptions(DIALOG_BUTTON_TYPE.WARNING, 'Сбросить', performResetSettings);
|
||
|
||
showInfo('Сбросить все настройки к значениям по умолчанию?', {
|
||
title: 'Подтверждение сброса',
|
||
buttons: [DIALOG_CANCEL_BUTTON, confirmButton]
|
||
});
|
||
}, [showInfo, performResetSettings]);
|
||
|
||
//Оптимизация базы данных
|
||
const handleOptimizeDb = React.useCallback(async () => {
|
||
setIsLoading(true);
|
||
try {
|
||
const success = await vacuum();
|
||
if (success) {
|
||
showSuccess('База данных оптимизирована');
|
||
} else {
|
||
showError('Не удалось оптимизировать базу данных');
|
||
}
|
||
} catch (error) {
|
||
showError('Не удалось оптимизировать базу данных');
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
}, [vacuum, showSuccess, showError]);
|
||
|
||
//Показ информации о приложении
|
||
const handleShowAppInfo = React.useCallback(() => {
|
||
const appInfo = getAppInfo({
|
||
mode,
|
||
serverUrl,
|
||
isDbReady
|
||
});
|
||
|
||
showInfo(appInfo, {
|
||
title: APP_ABOUT_TITLE
|
||
});
|
||
}, [mode, serverUrl, isDbReady, showInfo]);
|
||
|
||
//Обработчик кнопки назад
|
||
const handleBackPress = React.useCallback(() => {
|
||
if (canGoBack) {
|
||
goBack();
|
||
}
|
||
}, [canGoBack, goBack]);
|
||
|
||
//Обработчик копирования адреса сервера
|
||
const handleCopyServerUrl = React.useCallback(() => {
|
||
showSuccess('Адрес сервера скопирован');
|
||
}, [showSuccess]);
|
||
|
||
//Обработчик ошибки копирования
|
||
const handleCopyError = React.useCallback(() => {
|
||
showError('Не удалось скопировать адрес');
|
||
}, [showError]);
|
||
|
||
//Обработчик копирования идентификатора устройства
|
||
const handleCopyDeviceId = React.useCallback(() => {
|
||
showSuccess('Идентификатор устройства скопирован');
|
||
}, [showSuccess]);
|
||
|
||
return (
|
||
<AdaptiveView padding={false}>
|
||
<AppHeader showBackButton={true} onBackPress={handleBackPress} showMenuButton={false} />
|
||
|
||
<ScrollView style={styles.scrollView} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
||
{isAuthenticated && session ? (
|
||
<View style={styles.section}>
|
||
<AppText style={styles.sectionTitle} variant="h3" weight="semibold">
|
||
Информация о подключении
|
||
</AppText>
|
||
|
||
<View style={styles.infoRow}>
|
||
<AppText style={styles.infoLabel} variant="body">
|
||
Имя пользователя:
|
||
</AppText>
|
||
<AppText style={styles.infoValue} variant="body" weight="medium">
|
||
{session.userName || session.userCode || 'Не указано'}
|
||
</AppText>
|
||
</View>
|
||
|
||
<View style={styles.infoRow}>
|
||
<AppText style={styles.infoLabel} variant="body">
|
||
Организация:
|
||
</AppText>
|
||
<AppText style={styles.infoValue} variant="body" weight="medium">
|
||
{session.companyName || 'Не указана'}
|
||
</AppText>
|
||
</View>
|
||
</View>
|
||
) : null}
|
||
|
||
<View style={styles.section}>
|
||
<AppText style={styles.sectionTitle} variant="h3" weight="semibold">
|
||
Сервер приложений
|
||
</AppText>
|
||
|
||
<AppText style={styles.fieldLabel} variant="caption" weight="medium">
|
||
URL сервера
|
||
</AppText>
|
||
|
||
<View style={styles.serverUrlRow}>
|
||
<Pressable style={getServerUrlFieldPressableStyle} onPress={handleOpenServerUrlDialog} disabled={!isServerUrlEditable}>
|
||
<AppText
|
||
style={[
|
||
styles.serverUrlText,
|
||
!serverUrl && styles.serverUrlPlaceholder,
|
||
!isServerUrlEditable && styles.serverUrlTextDisabled
|
||
]}
|
||
numberOfLines={1}
|
||
>
|
||
{serverUrl || 'Нажмите для ввода адреса'}
|
||
</AppText>
|
||
</Pressable>
|
||
|
||
{serverUrl ? (
|
||
<CopyButton value={serverUrl} onCopy={handleCopyServerUrl} onError={handleCopyError} style={styles.serverUrlCopyButton} />
|
||
) : null}
|
||
</View>
|
||
|
||
<View style={styles.switchRow}>
|
||
<AppSwitch
|
||
label="Не отображать адрес сервера в окне логина"
|
||
value={hideServerUrl}
|
||
onValueChange={handleToggleHideServerUrl}
|
||
disabled={isLoading || !isDbReady}
|
||
/>
|
||
</View>
|
||
</View>
|
||
|
||
{mode === APP_MODE.ONLINE || mode === APP_MODE.OFFLINE ? (
|
||
<View style={styles.section}>
|
||
<AppText style={styles.sectionTitle} variant="h3" weight="semibold">
|
||
Главный экран
|
||
</AppText>
|
||
|
||
<View style={styles.switchRow}>
|
||
<AppSwitch
|
||
label={SCANNER_SETTING_LABEL}
|
||
value={alwaysShowScanner}
|
||
onValueChange={handleToggleAlwaysShowScanner}
|
||
disabled={isLoading || !isDbReady}
|
||
/>
|
||
</View>
|
||
|
||
{alwaysShowScanner ? (
|
||
<View style={styles.prioritySection}>
|
||
<AppText style={styles.fieldLabel} variant="caption" weight="medium">
|
||
{SCANNER_PRIORITY_LABEL}
|
||
</AppText>
|
||
<Pressable
|
||
style={[styles.priorityRow, mainScannerPriority === 'camera' && styles.priorityRowSelected]}
|
||
onPress={handleSelectCameraPriority}
|
||
disabled={isLoading || !isDbReady}
|
||
>
|
||
<AppText style={styles.priorityRowText}>{SCANNER_PRIORITY_CAMERA}</AppText>
|
||
{mainScannerPriority === 'camera' ? <AppText style={styles.priorityCheck}>✓</AppText> : null}
|
||
</Pressable>
|
||
<Pressable
|
||
style={[styles.priorityRow, mainScannerPriority === 'hardware' && styles.priorityRowSelected]}
|
||
onPress={handleSelectHardwarePriority}
|
||
disabled={isLoading || !isDbReady}
|
||
>
|
||
<AppText style={styles.priorityRowText}>{SCANNER_PRIORITY_HARDWARE}</AppText>
|
||
{mainScannerPriority === 'hardware' ? <AppText style={styles.priorityCheck}>✓</AppText> : null}
|
||
</Pressable>
|
||
</View>
|
||
) : null}
|
||
</View>
|
||
) : null}
|
||
|
||
<View style={styles.section}>
|
||
<AppText style={styles.sectionTitle} variant="h3" weight="semibold">
|
||
Системные настройки
|
||
</AppText>
|
||
|
||
<AppText style={styles.fieldLabel} variant="caption" weight="medium">
|
||
Максимальное время простоя (минут)
|
||
</AppText>
|
||
|
||
<Pressable style={getIdleTimeoutFieldPressableStyle} onPress={handleOpenIdleTimeoutDialog} disabled={isLoading || !isDbReady}>
|
||
<AppText style={[styles.serverUrlText, !idleTimeout && styles.serverUrlPlaceholder]} numberOfLines={1}>
|
||
{idleTimeout || 'Не задано'}
|
||
</AppText>
|
||
</Pressable>
|
||
|
||
<AppText style={[styles.fieldLabel, styles.fieldLabelMarginTop]} variant="caption" weight="medium">
|
||
Максимальное время ожидания ответа от сервера (секунд)
|
||
</AppText>
|
||
|
||
<Pressable
|
||
style={getServerRequestTimeoutFieldPressableStyle}
|
||
onPress={handleOpenServerRequestTimeoutDialog}
|
||
disabled={isLoading || !isDbReady}
|
||
>
|
||
<AppText style={styles.serverUrlText} numberOfLines={1}>
|
||
{serverRequestTimeout || String(DEFAULT_SERVER_REQUEST_TIMEOUT)}
|
||
</AppText>
|
||
</Pressable>
|
||
|
||
<AppText style={[styles.fieldLabel, styles.fieldLabelMarginTop]} variant="caption" weight="medium">
|
||
Идентификатор устройства
|
||
</AppText>
|
||
|
||
<View style={styles.serverUrlRow}>
|
||
<View style={[styles.serverUrlField, styles.deviceIdField]}>
|
||
<AppText style={styles.serverUrlText} numberOfLines={1}>
|
||
{deviceId || 'Загрузка...'}
|
||
</AppText>
|
||
</View>
|
||
|
||
{deviceId ? (
|
||
<CopyButton value={deviceId} onCopy={handleCopyDeviceId} onError={handleCopyError} style={styles.serverUrlCopyButton} />
|
||
) : null}
|
||
</View>
|
||
</View>
|
||
|
||
<View style={styles.section}>
|
||
<AppText style={styles.sectionTitle} variant="h3" weight="semibold">
|
||
Управление данными
|
||
</AppText>
|
||
|
||
<AppButton
|
||
title="Очистить кэш"
|
||
onPress={handleClearCache}
|
||
disabled={!isDbReady}
|
||
style={[styles.actionButton, styles.clearCacheButton]}
|
||
textStyle={styles.clearCacheButtonText}
|
||
/>
|
||
|
||
<AppButton
|
||
title="Сбросить настройки"
|
||
onPress={handleResetSettings}
|
||
disabled={!isDbReady}
|
||
style={[styles.actionButton, styles.resetButton]}
|
||
textStyle={styles.resetButtonText}
|
||
/>
|
||
|
||
<AppButton
|
||
title="Оптимизировать БД"
|
||
onPress={handleOptimizeDb}
|
||
disabled={isLoading || !isDbReady}
|
||
style={[styles.actionButton, styles.optimizeButton]}
|
||
textStyle={styles.optimizeButtonText}
|
||
/>
|
||
</View>
|
||
|
||
<View style={styles.section}>
|
||
<AppText style={styles.sectionTitle} variant="h3" weight="semibold">
|
||
Информация
|
||
</AppText>
|
||
|
||
<AppButton title={MENU_ITEM_ABOUT} onPress={handleShowAppInfo} style={[styles.actionButton, styles.infoButton]} />
|
||
|
||
<View style={styles.infoRow}>
|
||
<AppText style={styles.infoLabel} variant="body">
|
||
Текущий режим:
|
||
</AppText>
|
||
<AppText style={styles.infoValue} variant="body" weight="medium">
|
||
{getModeLabel(mode)}
|
||
</AppText>
|
||
</View>
|
||
|
||
<View style={styles.infoRow}>
|
||
<AppText style={styles.infoLabel} variant="body">
|
||
Адрес сервера:
|
||
</AppText>
|
||
<View style={styles.infoValueWithAction}>
|
||
<AppText style={styles.infoValueFlex} variant="body" weight="medium" numberOfLines={1}>
|
||
{serverUrl || 'Не настроен'}
|
||
</AppText>
|
||
{serverUrl ? (
|
||
<CopyButton value={serverUrl} onCopy={handleCopyServerUrl} onError={handleCopyError} style={styles.copyButton} />
|
||
) : null}
|
||
</View>
|
||
</View>
|
||
|
||
<View style={styles.infoRow}>
|
||
<AppText style={styles.infoLabel} variant="body">
|
||
База данных:
|
||
</AppText>
|
||
<AppText style={styles.infoValue} variant="body" weight="medium">
|
||
{isDbReady ? 'Готова' : 'Загрузка...'}
|
||
</AppText>
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
|
||
<InputDialog
|
||
ref={serverUrlDialogRef}
|
||
visible={isServerUrlDialogVisible}
|
||
title="Адрес сервера"
|
||
label="URL сервера приложений"
|
||
value={serverUrl}
|
||
placeholder="https://example.com/api"
|
||
keyboardType="url"
|
||
confirmText="Сохранить"
|
||
cancelText="Отмена"
|
||
onConfirm={handleSaveServerUrl}
|
||
onCancel={handleCloseServerUrlDialog}
|
||
validator={validateServerUrlAllowEmpty}
|
||
/>
|
||
|
||
<InputDialog
|
||
ref={idleTimeoutDialogRef}
|
||
visible={isIdleTimeoutDialogVisible}
|
||
title="Время простоя"
|
||
label="Максимальное время простоя (минут)"
|
||
value={idleTimeout}
|
||
placeholder="Например: 30 или пусто"
|
||
keyboardType="numeric"
|
||
confirmText="Сохранить"
|
||
cancelText="Отмена"
|
||
onConfirm={handleSaveIdleTimeout}
|
||
onCancel={handleCloseIdleTimeoutDialog}
|
||
validator={validateIdleTimeoutAllowEmpty}
|
||
/>
|
||
|
||
<InputDialog
|
||
ref={serverRequestTimeoutDialogRef}
|
||
visible={isServerRequestTimeoutDialogVisible}
|
||
title="Время ожидания сервера"
|
||
label="Максимальное время ожидания ответа от сервера (секунд)"
|
||
value={serverRequestTimeout}
|
||
placeholder="Например: 60"
|
||
keyboardType="numeric"
|
||
confirmText="Сохранить"
|
||
cancelText="Отмена"
|
||
onConfirm={handleSaveServerRequestTimeout}
|
||
onCancel={handleCloseServerRequestTimeoutDialog}
|
||
validator={validateServerRequestTimeout}
|
||
/>
|
||
</AdaptiveView>
|
||
);
|
||
}
|
||
|
||
//----------------
|
||
//Интерфейс модуля
|
||
//----------------
|
||
|
||
module.exports = SettingsScreen;
|