Инициализация директории приложения, формирование структуры и основных компонентов приложения
This commit is contained in:
parent
20753eb9e3
commit
561763dc74
36
rn/app/.eslintrc.js
Normal file
36
rn/app/.eslintrc.js
Normal file
@ -0,0 +1,36 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: '@react-native',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2021,
|
||||
sourceType: 'module',
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
babelOptions: {
|
||||
configFile: path.resolve(__dirname, 'babel.config.js'),
|
||||
},
|
||||
},
|
||||
env: {
|
||||
'react-native/react-native': true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.js', '*.jsx'],
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
babelOptions: {
|
||||
configFile: path.resolve(__dirname, 'babel.config.js'),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
787
rn/app/.gitignore
vendored
Normal file
787
rn/app/.gitignore
vendored
Normal file
@ -0,0 +1,787 @@
|
||||
# -------------------------------------------------------------------
|
||||
# OPERATING SYSTEM FILES
|
||||
# -------------------------------------------------------------------
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
._*
|
||||
.fseventsd
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
*.lnk
|
||||
|
||||
# Linux
|
||||
*~
|
||||
.directory
|
||||
*.swp
|
||||
*.swo
|
||||
*.pid
|
||||
.session
|
||||
.viminfo
|
||||
|
||||
# Cross-platform
|
||||
.nfs*
|
||||
*.lock
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# IDE AND EDITOR CONFIGURATIONS
|
||||
# -------------------------------------------------------------------
|
||||
# VS Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.vscode/*.code-workspace
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
*.iws
|
||||
*.ipr
|
||||
modules.xml
|
||||
workspace.xml
|
||||
|
||||
# Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
.cache/
|
||||
.metadata/
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
|
||||
# Sublime Text
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
*.sublime-gps
|
||||
*.sublime-gps-cache
|
||||
|
||||
# Atom
|
||||
.atom/
|
||||
.atomic/
|
||||
.blobs/
|
||||
.compile-cache/
|
||||
.node-gyp/
|
||||
.npm/
|
||||
.snap/
|
||||
.storage/
|
||||
|
||||
# Emacs
|
||||
*~
|
||||
\#*\#
|
||||
.\#*
|
||||
auto-save-list
|
||||
tramp
|
||||
.#*
|
||||
|
||||
# Vim
|
||||
*.swp
|
||||
*.swo
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# NODE.JS AND JAVASCRIPT
|
||||
# -------------------------------------------------------------------
|
||||
# Dependencies
|
||||
node_modules/
|
||||
.node_modules/
|
||||
npm-packages-offline-cache/
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Grunt intermediate storage
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory
|
||||
bower_components/
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# Yarn 2/3
|
||||
.yarn/*
|
||||
!.yarn/cache
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
.yarnrc.yml
|
||||
|
||||
# pnpm
|
||||
node_modules/.pnpm-store/
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Turbo
|
||||
.turbo/
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# REACT NATIVE SPECIFIC
|
||||
# -------------------------------------------------------------------
|
||||
.expo/
|
||||
.expo-shared/
|
||||
.expo-cache/
|
||||
.expo-web-cache/
|
||||
|
||||
# Metro bundler cache
|
||||
.rn-cache/
|
||||
.metrobundler/
|
||||
.metro/
|
||||
metro.config.js.timestamp
|
||||
metro-cache/
|
||||
.haste-map-*
|
||||
.haste-cache/
|
||||
|
||||
# React Native CLI
|
||||
.react-native/
|
||||
.rncli/
|
||||
|
||||
# Build artifacts
|
||||
.release-please-manifest.json
|
||||
.release-it.json
|
||||
.changelog/
|
||||
|
||||
# Hermes debugger
|
||||
.hermes/
|
||||
|
||||
# Fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# ANDROID (Comprehensive)
|
||||
# -------------------------------------------------------------------
|
||||
# Build
|
||||
android/**/build/
|
||||
android/**/.cxx/
|
||||
android/**/.gradle/
|
||||
android/**/.idea/
|
||||
android/**/local.properties
|
||||
android/**/*.iml
|
||||
android/**/*.iws
|
||||
android/**/*.ipr
|
||||
|
||||
# Generated files
|
||||
android/**/generated/
|
||||
android/**/intermediates/
|
||||
android/**/outputs/
|
||||
android/**/tmp/
|
||||
android/**/.apt_generated/
|
||||
|
||||
# Keystores and certificates
|
||||
android/**/*.keystore
|
||||
android/**/*.jks
|
||||
android/**/*.pk8
|
||||
android/**/*.pem
|
||||
android/**/*.der
|
||||
android/**/*.pfx
|
||||
android/**/*.p12
|
||||
android/**/debug.keystore
|
||||
android/**/release.keystore
|
||||
|
||||
# Gradle
|
||||
android/**/.gradle/
|
||||
android/**/gradle/
|
||||
android/**/gradlew
|
||||
android/**/gradlew.bat
|
||||
android/**/gradle/wrapper/gradle-wrapper.jar
|
||||
android/**/gradle/wrapper/gradle-wrapper.properties
|
||||
android/**/caches/
|
||||
android/**/wrapper/dists/
|
||||
|
||||
# Android Studio
|
||||
android/**/.android/
|
||||
android/**/captures/
|
||||
android/**/*.hprof
|
||||
|
||||
# NDK
|
||||
android/**/.externalNativeBuild/
|
||||
android/**/obj/
|
||||
android/**/libs/
|
||||
android/**/jniLibs/
|
||||
android/**/*.so
|
||||
android/**/*.o
|
||||
android/**/*.a
|
||||
|
||||
# Autolinking
|
||||
android/**/src/main/java/com/facebook/react/
|
||||
android/**/src/main/jni/
|
||||
|
||||
# CMake
|
||||
android/**/.cxx/
|
||||
android/**/CMakeFiles/
|
||||
android/**/CMakeCache.txt
|
||||
android/**/cmake_install.cmake
|
||||
android/**/Makefile
|
||||
android/**/*.ninja
|
||||
android/**/.ninja_deps
|
||||
android/**/.ninja_log
|
||||
android/**/compile_commands.json
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# iOS (Comprehensive)
|
||||
# -------------------------------------------------------------------
|
||||
# Build
|
||||
ios/**/build/
|
||||
ios/**/DerivedData/
|
||||
ios/**/xcuserdata/
|
||||
ios/**/.xcode/
|
||||
|
||||
# Xcode
|
||||
*.xcodeproj/*
|
||||
!*.xcodeproj/project.pbxproj
|
||||
!*.xcodeproj/xcshareddata/
|
||||
!*.xcodeproj/xcshareddata/xcschemes/
|
||||
*.xcodeproj/xcuserdata/
|
||||
*.xcodeproj/project.xcworkspace/xcuserdata/
|
||||
*.xcodeproj/xcshareddata/IDEWorkspaceChecks.plist
|
||||
|
||||
# Xcode workspaces
|
||||
*.xcworkspace/*
|
||||
!*.xcworkspace/contents.xcworkspacedata
|
||||
*.xcworkspace/xcuserdata/
|
||||
|
||||
# CocoaPods
|
||||
ios/**/Pods/
|
||||
Podfile.lock
|
||||
ios/**/Podfile.lock
|
||||
ios/**/Manifest.lock
|
||||
ios/**/Podfile.local
|
||||
|
||||
# Carthage
|
||||
Carthage/
|
||||
ios/**/Carthage/
|
||||
|
||||
# Swift Package Manager
|
||||
.swiftpm/
|
||||
Package.resolved
|
||||
ios/**/.build/
|
||||
ios/**/.swiftpm/
|
||||
|
||||
# Swift/Objective-C precompiled headers
|
||||
*.pch
|
||||
*.gch
|
||||
|
||||
# Compiled sources
|
||||
*.o
|
||||
*.obj
|
||||
*.dylib
|
||||
*.framework
|
||||
*.app
|
||||
*.ipa
|
||||
*.dSYM
|
||||
*.bcsymbolmap
|
||||
|
||||
# App-specific
|
||||
ios/**/app-tvos.app
|
||||
ios/**/app-tvostests.xctest
|
||||
ios/**/app.app
|
||||
ios/**/appTests.xctest
|
||||
ios/**/*.mode1v3
|
||||
ios/**/*.mode2v3
|
||||
ios/**/*.moved-aside
|
||||
ios/**/*.pbxuser
|
||||
!ios/**/default.pbxuser
|
||||
ios/**/*.perspectivev3
|
||||
|
||||
# Playgrounds
|
||||
*.playground/
|
||||
|
||||
# SwiftLint
|
||||
.swiftlint.yml
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# WEB PLATFORM
|
||||
# -------------------------------------------------------------------
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
.next/cache/
|
||||
|
||||
# Nuxt.js
|
||||
.nuxt/
|
||||
.nuxt-prod/
|
||||
|
||||
# Gatsby
|
||||
.cache/
|
||||
public
|
||||
|
||||
# Angular
|
||||
dist/
|
||||
aot/
|
||||
**/node_modules/
|
||||
|
||||
# Vue
|
||||
dist/
|
||||
|
||||
# Svelte
|
||||
.svelte-kit/
|
||||
/build/
|
||||
|
||||
# Parcel
|
||||
.parcel-cache/
|
||||
dist/
|
||||
|
||||
# Webpack
|
||||
.webpack/
|
||||
webpack-assets.json
|
||||
stats.json
|
||||
|
||||
# Rollup
|
||||
rollup.config-*.js
|
||||
|
||||
# Vite
|
||||
dist/
|
||||
*.local
|
||||
|
||||
# Serverless
|
||||
.serverless/
|
||||
.serverless_nextjs/
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# TESTING AND CODE QUALITY
|
||||
# -------------------------------------------------------------------
|
||||
# Jest
|
||||
coverage/
|
||||
.nyc_output/
|
||||
*.lcov
|
||||
jest-coverage/
|
||||
__snapshots__/
|
||||
*.snap
|
||||
|
||||
# Cypress
|
||||
cypress/videos/
|
||||
cypress/screenshots/
|
||||
cypress/downloads/
|
||||
cypress/fixtures/example.json
|
||||
|
||||
# Playwright
|
||||
playwright-report/
|
||||
test-results/
|
||||
playwright/.cache/
|
||||
|
||||
# Detox
|
||||
artifacts/
|
||||
*.detoxrc.js
|
||||
|
||||
# Code coverage
|
||||
.clover
|
||||
cobertura.xml
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.lcov
|
||||
|
||||
# Linters
|
||||
.eslintcache
|
||||
.stylelintcache
|
||||
.tsbuildinfo
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# TYPE/FLOW/JAVASCRIPT COMPILATION
|
||||
# -------------------------------------------------------------------
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
dist-tsc/
|
||||
.build/
|
||||
*.d.ts
|
||||
*.js.map
|
||||
*.ts.map
|
||||
|
||||
# Flow
|
||||
.flowconfig
|
||||
.flow-typed/
|
||||
|
||||
# Babel
|
||||
*.babelcache
|
||||
lib-cov/
|
||||
|
||||
# SWC
|
||||
.swc/
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# ENVIRONMENT AND CONFIGURATION
|
||||
# -------------------------------------------------------------------
|
||||
# Environment variables
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
.env.local
|
||||
.env.development
|
||||
.env.production
|
||||
.env.test
|
||||
.env.staging
|
||||
.env.ci
|
||||
|
||||
# Firebase
|
||||
.firebase/
|
||||
.firebaserc
|
||||
|
||||
# Docker
|
||||
.dockerignore
|
||||
docker-compose.override.yml
|
||||
docker-compose.*.yml
|
||||
docker-compose.yml
|
||||
|
||||
# CI/CD
|
||||
.circleci/config.yml
|
||||
.github/workflows/
|
||||
.gitlab-ci.yml
|
||||
.jenkins/
|
||||
.travis.yml
|
||||
.azure-pipelines/
|
||||
appveyor.yml
|
||||
.codeship/
|
||||
.shippable.yml
|
||||
.semaphore/
|
||||
.buildkite/
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# DATABASES AND STORAGE
|
||||
# -------------------------------------------------------------------
|
||||
# SQLite
|
||||
*.db
|
||||
*.db-journal
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
# Realm
|
||||
*.realm
|
||||
*.realm.*
|
||||
|
||||
# WatermelonDB
|
||||
*.watermelondb
|
||||
|
||||
# PouchDB
|
||||
_pouch_
|
||||
|
||||
# LocalStorage backups
|
||||
*.localstorage
|
||||
*.localstorage-journal
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# MEDIA AND ASSETS (GENERATED/COMPRESSED)
|
||||
# -------------------------------------------------------------------
|
||||
# Images (generated/compressed)
|
||||
*.jpg
|
||||
*.jpeg
|
||||
*.png
|
||||
*.gif
|
||||
*.webp
|
||||
*.ico
|
||||
*.bmp
|
||||
*.tiff
|
||||
|
||||
# Videos
|
||||
*.mp4
|
||||
*.mov
|
||||
*.avi
|
||||
*.mkv
|
||||
|
||||
# Audio
|
||||
*.mp3
|
||||
*.wav
|
||||
*.ogg
|
||||
|
||||
# Fonts (generated)
|
||||
*.ttf
|
||||
*.otf
|
||||
*.woff
|
||||
*.woff2
|
||||
*.eot
|
||||
|
||||
# Archives
|
||||
*.zip
|
||||
*.tar
|
||||
*.gz
|
||||
*.bz2
|
||||
*.7z
|
||||
*.rar
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# DOCUMENTATION
|
||||
# -------------------------------------------------------------------
|
||||
_site/
|
||||
docs/_build/
|
||||
docs/_site/
|
||||
docs/api/
|
||||
docs/coverage/
|
||||
docs/build/
|
||||
docs/dist/
|
||||
|
||||
# MkDocs
|
||||
site/
|
||||
|
||||
# JSDoc
|
||||
out-jsdoc/
|
||||
|
||||
# Docusaurus
|
||||
.docusaurus/
|
||||
build/
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# PACKAGE MANAGERS AND LOCKFILES
|
||||
# -------------------------------------------------------------------
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
shrinkwrap.yaml
|
||||
npm-shrinkwrap.json
|
||||
|
||||
# Ruby
|
||||
Gemfile.lock
|
||||
.bundle/
|
||||
vendor/bundle/
|
||||
|
||||
# Python
|
||||
Pipfile.lock
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.python-version
|
||||
|
||||
# Go
|
||||
go.sum
|
||||
go.mod
|
||||
vendor/
|
||||
|
||||
# Rust
|
||||
Cargo.lock
|
||||
target/
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# LOGS AND TEMPORARY FILES
|
||||
# -------------------------------------------------------------------
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
firebase-debug.log*
|
||||
metro-bundler-*.log
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
*.temp
|
||||
*.bak
|
||||
*.backup
|
||||
*.old
|
||||
|
||||
# Crash logs
|
||||
*.crash
|
||||
*.error
|
||||
*.dump
|
||||
|
||||
# Performance logs
|
||||
*.perf
|
||||
*.profile
|
||||
*.heapsnapshot
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# MISC DEVELOPMENT FILES
|
||||
# -------------------------------------------------------------------
|
||||
# Storybook
|
||||
.storybook/
|
||||
storybook-static/
|
||||
|
||||
# Chromatic
|
||||
.chromatic/
|
||||
|
||||
# Sentry
|
||||
.sentry/
|
||||
.sentryclirc
|
||||
|
||||
# Mixpanel
|
||||
.mixpanel/
|
||||
|
||||
# Analytics
|
||||
.amplify/
|
||||
.aws/
|
||||
|
||||
# ML/AI
|
||||
*.pt
|
||||
*.pth
|
||||
*.h5
|
||||
*.keras
|
||||
.tensorflow/
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# EXCEPTIONS (MUST BE KEPT IN REPO)
|
||||
# -------------------------------------------------------------------
|
||||
# IMPORTANT: These files SHOULD be committed
|
||||
!android/app/src/main/assets/
|
||||
!android/app/src/main/res/
|
||||
!android/app/src/main/java/com/app/
|
||||
!android/app/src/main/AndroidManifest.xml
|
||||
!android/app/src/main/res/values/strings.xml
|
||||
!android/app/src/main/res/values/styles.xml
|
||||
!android/app/build.gradle
|
||||
!android/build.gradle
|
||||
!android/settings.gradle
|
||||
!android/gradle.properties
|
||||
!android/gradlew
|
||||
!android/gradlew.bat
|
||||
!android/gradle/wrapper/gradle-wrapper.jar
|
||||
!android/gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
!ios/app/
|
||||
!ios/App/
|
||||
!ios/app.xcodeproj/project.pbxproj
|
||||
!ios/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme
|
||||
!ios/app/AppDelegate.swift
|
||||
!ios/app/Info.plist
|
||||
!ios/app/Assets.xcassets/
|
||||
!ios/app/LaunchScreen.storyboard
|
||||
!ios/Podfile
|
||||
|
||||
!src/
|
||||
!App.js
|
||||
!App.tsx
|
||||
!index.js
|
||||
!index.ts
|
||||
!app.json
|
||||
!babel.config.js
|
||||
!metro.config.js
|
||||
!jest.config.js
|
||||
!tsconfig.json
|
||||
!.eslintrc.js
|
||||
!.prettierrc.js
|
||||
!.watchmanconfig
|
||||
!.bundle/config
|
||||
!Gemfile
|
||||
!__tests__/
|
||||
!package.json
|
||||
|
||||
!README.md
|
||||
!LICENSE
|
||||
!CHANGELOG.md
|
||||
!CONTRIBUTING.md
|
||||
!SECURITY.md
|
||||
!CODE_OF_CONDUCT.md
|
||||
|
||||
# Git itself (but allow .git directory)
|
||||
!.git/
|
||||
!.git/**
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# PRODUCTION-SPECIFIC IGNORES
|
||||
# -------------------------------------------------------------------
|
||||
# Source maps (can be generated during build)
|
||||
*.map
|
||||
|
||||
# Bundle files (generated during build)
|
||||
*.bundle
|
||||
*.jsbundle
|
||||
|
||||
# Hermes bytecode
|
||||
*.hbc
|
||||
|
||||
# App binaries
|
||||
*.apk
|
||||
*.aab
|
||||
*.ipa
|
||||
*.app
|
||||
*.dmg
|
||||
|
||||
# App signing
|
||||
*.mobileprovision
|
||||
*.p12
|
||||
*.cer
|
||||
|
||||
# Crashlytics
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Google Services
|
||||
google-services.json
|
||||
GoogleService-Info.plist
|
||||
|
||||
# Microsoft App Center
|
||||
appcenter-config.json
|
||||
|
||||
# OneSignal
|
||||
OneSignal.plugin
|
||||
|
||||
# RevenueCat
|
||||
RevenueCat.plugin
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# SECURITY-CRITICAL FILES (NEVER COMMIT)
|
||||
# -------------------------------------------------------------------
|
||||
# API Keys
|
||||
*.apikey
|
||||
*.apisecret
|
||||
*.secret
|
||||
*.token
|
||||
*.credential
|
||||
|
||||
# SSH Keys
|
||||
*.pem
|
||||
*.key
|
||||
*.pub
|
||||
id_rsa
|
||||
id_dsa
|
||||
*.ppk
|
||||
|
||||
# Certificates
|
||||
*.crt
|
||||
*.cert
|
||||
*.pfx
|
||||
*.p12
|
||||
|
||||
# Passwords
|
||||
*.password
|
||||
*.pass
|
||||
*.pwd
|
||||
|
||||
# Configuration with secrets
|
||||
config.local.*
|
||||
secret.*
|
||||
private.*
|
||||
prod.*
|
||||
staging.*
|
||||
8
rn/app/.prettierrc.js
Normal file
8
rn/app/.prettierrc.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
arrowParens: 'avoid',
|
||||
singleQuote: true,
|
||||
trailingComma: 'none',
|
||||
"useTabs": false,
|
||||
"tabWidth": 4,
|
||||
"printWidth": 150,
|
||||
};
|
||||
1
rn/app/.watchmanconfig
Normal file
1
rn/app/.watchmanconfig
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
49
rn/app/App.js
Normal file
49
rn/app/App.js
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Корневой файл приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const AppErrorBoundary = require('./src/components/layout/AppErrorBoundary'); //Обработчик ошибок
|
||||
const AppMessagingProvider = require('./src/components/layout/AppMessagingProvider').AppMessagingProvider; //Провайдер сообщений
|
||||
const AppNavigationProvider = require('./src/components/layout/AppNavigationProvider').AppNavigationProvider; //Провайдер навигации
|
||||
const AppModeProvider = require('./src/components/layout/AppModeProvider').AppModeProvider; //Провайдер режима работы
|
||||
const AppLocalDbProvider = require('./src/components/layout/AppLocalDbProvider').AppLocalDbProvider; //Провайдер локальной БД
|
||||
const AppServerProvider = require('./src/components/layout/AppServerProvider').AppServerProvider; //Провайдер сервера
|
||||
const AppPreTripInspectionsProvider = require('./src/components/layout/AppPreTripInspectionsProvider').AppPreTripInspectionsProvider; //Провайдер осмотров
|
||||
const AppRoot = require('./src/components/layout/AppRoot'); //Корневой layout приложения
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Основное приложение
|
||||
function App() {
|
||||
return (
|
||||
<AppErrorBoundary>
|
||||
<AppMessagingProvider>
|
||||
<AppNavigationProvider>
|
||||
<AppLocalDbProvider>
|
||||
<AppModeProvider>
|
||||
<AppServerProvider>
|
||||
<AppPreTripInspectionsProvider>
|
||||
<AppRoot />
|
||||
</AppPreTripInspectionsProvider>
|
||||
</AppServerProvider>
|
||||
</AppModeProvider>
|
||||
</AppLocalDbProvider>
|
||||
</AppNavigationProvider>
|
||||
</AppMessagingProvider>
|
||||
</AppErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = App;
|
||||
16
rn/app/Gemfile
Normal file
16
rn/app/Gemfile
Normal file
@ -0,0 +1,16 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby ">= 2.6.10"
|
||||
|
||||
# Exclude problematic versions of cocoapods and activesupport that causes build failures.
|
||||
gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
|
||||
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
|
||||
gem 'xcodeproj', '< 1.26.0'
|
||||
gem 'concurrent-ruby', '< 1.3.4'
|
||||
|
||||
# Ruby 3.4.0 has removed some libraries from the standard library.
|
||||
gem 'bigdecimal'
|
||||
gem 'logger'
|
||||
gem 'benchmark'
|
||||
gem 'mutex_m'
|
||||
13
rn/app/__tests__/App.test.tsx
Normal file
13
rn/app/__tests__/App.test.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactTestRenderer from 'react-test-renderer';
|
||||
import App from '../App';
|
||||
|
||||
test('renders correctly', async () => {
|
||||
await ReactTestRenderer.act(() => {
|
||||
ReactTestRenderer.create(<App />);
|
||||
});
|
||||
});
|
||||
119
rn/app/android/app/build.gradle
Normal file
119
rn/app/android/app/build.gradle
Normal file
@ -0,0 +1,119 @@
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: "com.facebook.react"
|
||||
|
||||
/**
|
||||
* This is the configuration block to customize your React Native Android app.
|
||||
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
||||
*/
|
||||
react {
|
||||
/* Folders */
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '../..'
|
||||
// root = file("../../")
|
||||
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
|
||||
// reactNativeDir = file("../../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
|
||||
// codegenDir = file("../../node_modules/@react-native/codegen")
|
||||
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
|
||||
// cliFile = file("../../node_modules/react-native/cli.js")
|
||||
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||
|
||||
/* Bundling */
|
||||
// A list containing the node command and its flags. Default is just 'node'.
|
||||
// nodeExecutableAndArgs = ["node"]
|
||||
//
|
||||
// The command to run when bundling. By default is 'bundle'
|
||||
// bundleCommand = "ram-bundle"
|
||||
//
|
||||
// The path to the CLI configuration file. Default is empty.
|
||||
// bundleConfig = file(../rn-cli.config.js)
|
||||
//
|
||||
// The name of the generated asset file containing your JS bundle
|
||||
// bundleAssetName = "MyApplication.android.bundle"
|
||||
//
|
||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||
// entryFile = file("../js/MyApplication.android.js")
|
||||
//
|
||||
// A list of extra flags to pass to the 'bundle' commands.
|
||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||
// extraPackagerArgs = []
|
||||
|
||||
/* Hermes Commands */
|
||||
// The hermes compiler command to run. By default it is 'hermesc'
|
||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
// hermesFlags = ["-O", "-output-source-map"]
|
||||
|
||||
/* Autolinking */
|
||||
autolinkLibrariesWithApp()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore (JSC)
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
|
||||
namespace "com.app"
|
||||
defaultConfig {
|
||||
applicationId "com.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
10
rn/app/android/app/proguard-rules.pro
vendored
Normal file
10
rn/app/android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
27
rn/app/android/app/src/main/AndroidManifest.xml
Normal file
27
rn/app/android/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="${usesCleartextTraffic}"
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
22
rn/app/android/app/src/main/java/com/app/MainActivity.kt
Normal file
22
rn/app/android/app/src/main/java/com/app/MainActivity.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package com.app
|
||||
|
||||
import com.facebook.react.ReactActivity
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
||||
|
||||
class MainActivity : ReactActivity() {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
||||
* rendering of the component.
|
||||
*/
|
||||
override fun getMainComponentName(): String = "Pre-Trip_Inspections"
|
||||
|
||||
/**
|
||||
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
|
||||
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
|
||||
*/
|
||||
override fun createReactActivityDelegate(): ReactActivityDelegate =
|
||||
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
|
||||
}
|
||||
27
rn/app/android/app/src/main/java/com/app/MainApplication.kt
Normal file
27
rn/app/android/app/src/main/java/com/app/MainApplication.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package com.app
|
||||
|
||||
import android.app.Application
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactHost
|
||||
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
|
||||
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
override val reactHost: ReactHost by lazy {
|
||||
getDefaultReactHost(
|
||||
context = applicationContext,
|
||||
packageList =
|
||||
PackageList(this).packages.apply {
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// add(MyReactNativePackage())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
loadReactNative(this)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"
|
||||
>
|
||||
|
||||
<selector>
|
||||
<!--
|
||||
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
||||
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
|
||||
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
|
||||
|
||||
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
|
||||
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
|
||||
-->
|
||||
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
|
||||
</selector>
|
||||
|
||||
</inset>
|
||||
3
rn/app/android/app/src/main/res/values/strings.xml
Normal file
3
rn/app/android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Парус© ПО</string>
|
||||
</resources>
|
||||
9
rn/app/android/app/src/main/res/values/styles.xml
Normal file
9
rn/app/android/app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
21
rn/app/android/build.gradle
Normal file
21
rn/app/android/build.gradle
Normal file
@ -0,0 +1,21 @@
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "36.0.0"
|
||||
minSdkVersion = 24
|
||||
compileSdkVersion = 36
|
||||
targetSdkVersion = 36
|
||||
ndkVersion = "27.1.12297006"
|
||||
kotlinVersion = "2.1.20"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle")
|
||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.facebook.react.rootproject"
|
||||
44
rn/app/android/gradle.properties
Normal file
44
rn/app/android/gradle.properties
Normal file
@ -0,0 +1,44 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
|
||||
# Use this property to enable support to the new architecture.
|
||||
# This will allow you to use TurboModules and the Fabric render in
|
||||
# your application. You should enable this flag either if you want
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
newArchEnabled=true
|
||||
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
hermesEnabled=true
|
||||
|
||||
# Use this property to enable edge-to-edge display support.
|
||||
# This allows your app to draw behind system bars for an immersive UI.
|
||||
# Note: Only works with ReactActivity and should not be used with custom Activity.
|
||||
edgeToEdgeEnabled=false
|
||||
251
rn/app/android/gradlew
vendored
Normal file
251
rn/app/android/gradlew
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
99
rn/app/android/gradlew.bat
vendored
Normal file
99
rn/app/android/gradlew.bat
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
@REM Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
@REM
|
||||
@REM This source code is licensed under the MIT license found in the
|
||||
@REM LICENSE file in the root directory of this source tree.
|
||||
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
6
rn/app/android/settings.gradle
Normal file
6
rn/app/android/settings.gradle
Normal file
@ -0,0 +1,6 @@
|
||||
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
|
||||
plugins { id("com.facebook.react.settings") }
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
||||
rootProject.name = 'app'
|
||||
include ':app'
|
||||
includeBuild('../node_modules/@react-native/gradle-plugin')
|
||||
4
rn/app/app.json
Normal file
4
rn/app/app.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "Pre-Trip_Inspections",
|
||||
"displayName": "Парус© Предрейсовые осмотры"
|
||||
}
|
||||
3
rn/app/babel.config.js
Normal file
3
rn/app/babel.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
};
|
||||
20
rn/app/index.js
Normal file
20
rn/app/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Точка входа приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const { AppRegistry } = require("react-native"); //Регистрация корневого компонента
|
||||
const App = require("./App"); //Корневой компонент приложения
|
||||
const { name: appName } = require("./app.json"); //Имя приложения
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Регистрация корневого компонента
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
|
||||
11
rn/app/ios/.xcode.env
Normal file
11
rn/app/ios/.xcode.env
Normal file
@ -0,0 +1,11 @@
|
||||
# This `.xcode.env` file is versioned and is used to source the environment
|
||||
# used when running script phases inside Xcode.
|
||||
# To customize your local environment, you can create an `.xcode.env.local`
|
||||
# file that is not versioned.
|
||||
|
||||
# NODE_BINARY variable contains the PATH to the node executable.
|
||||
#
|
||||
# Customize the NODE_BINARY variable here.
|
||||
# For example, to use nvm with brew, add the following line
|
||||
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
||||
export NODE_BINARY=$(command -v node)
|
||||
34
rn/app/ios/Podfile
Normal file
34
rn/app/ios/Podfile
Normal file
@ -0,0 +1,34 @@
|
||||
# Resolve react_native_pods.rb with node to allow for hoisting
|
||||
require Pod::Executable.execute_command('node', ['-p',
|
||||
'require.resolve(
|
||||
"react-native/scripts/react_native_pods.rb",
|
||||
{paths: [process.argv[1]]},
|
||||
)', __dir__]).strip
|
||||
|
||||
platform :ios, min_ios_version_supported
|
||||
prepare_react_native_project!
|
||||
|
||||
linkage = ENV['USE_FRAMEWORKS']
|
||||
if linkage != nil
|
||||
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
|
||||
use_frameworks! :linkage => linkage.to_sym
|
||||
end
|
||||
|
||||
target 'app' do
|
||||
config = use_native_modules!
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||
)
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(
|
||||
installer,
|
||||
config[:reactNativePath],
|
||||
:mac_catalyst_enabled => false,
|
||||
# :ccache_enabled => true
|
||||
)
|
||||
end
|
||||
end
|
||||
471
rn/app/ios/app.xcodeproj/project.pbxproj
Normal file
471
rn/app/ios/app.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,471 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0C80B921A6F3F58F76C31292 /* libPods-app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-app.a */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
13B07F961A680F5B00A75B9A /* app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = app/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = app/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = app/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
3B4392A12AC88292D35C810B /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
5709B34CF0A7D63546082F79 /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = "<group>"; };
|
||||
5DCACB8F33CDC322A6C60F78 /* libPods-app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = app/AppDelegate.swift; sourceTree = "<group>"; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = app/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0C80B921A6F3F58F76C31292 /* libPods-app.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
13B07FAE1A68108700A75B9A /* app */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
761780EC2CA45674006654EE /* AppDelegate.swift */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
|
||||
);
|
||||
name = app;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
5DCACB8F33CDC322A6C60F78 /* libPods-app.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* app */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
usesTabs = 0;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* app.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B4392A12AC88292D35C810B /* Pods-app.debug.xcconfig */,
|
||||
5709B34CF0A7D63546082F79 /* Pods-app.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
13B07F861A680F5B00A75B9A /* app */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "app" */;
|
||||
buildPhases = (
|
||||
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
|
||||
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = app;
|
||||
productName = app;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* app.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1210;
|
||||
TargetAttributes = {
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1120;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* app */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/.xcode.env.local",
|
||||
"$(SRCROOT)/.xcode.env",
|
||||
);
|
||||
name = "Bundle React Native code and images";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"\\\"$WITH_ENVIRONMENT\\\" \\\"$REACT_NATIVE_XCODE\\\"\"\n";
|
||||
};
|
||||
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-app-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-app.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = app/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = app;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-app.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = app/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = app;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
/usr/lib/swift,
|
||||
"$(inherited)",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-DFOLLY_NO_CONFIG",
|
||||
"-DFOLLY_MOBILE=1",
|
||||
"-DFOLLY_USE_LIBCPP=1",
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
83CBBA211A601CBA00E9B192 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
/usr/lib/swift,
|
||||
"$(inherited)",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-DFOLLY_NO_CONFIG",
|
||||
"-DFOLLY_MOBILE=1",
|
||||
"-DFOLLY_USE_LIBCPP=1",
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
13B07F941A680F5B00A75B9A /* Debug */,
|
||||
13B07F951A680F5B00A75B9A /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83CBBA201A601CBA00E9B192 /* Debug */,
|
||||
83CBBA211A601CBA00E9B192 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
}
|
||||
88
rn/app/ios/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme
Normal file
88
rn/app/ios/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme
Normal file
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1210"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "app.app"
|
||||
BlueprintName = "app"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||
BuildableName = "appTests.xctest"
|
||||
BlueprintName = "appTests"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "app.app"
|
||||
BlueprintName = "app"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "app.app"
|
||||
BlueprintName = "app"
|
||||
ReferencedContainer = "container:app.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
48
rn/app/ios/app/AppDelegate.swift
Normal file
48
rn/app/ios/app/AppDelegate.swift
Normal file
@ -0,0 +1,48 @@
|
||||
import UIKit
|
||||
import React
|
||||
import React_RCTAppDelegate
|
||||
import ReactAppDependencyProvider
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
var reactNativeDelegate: ReactNativeDelegate?
|
||||
var reactNativeFactory: RCTReactNativeFactory?
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool {
|
||||
let delegate = ReactNativeDelegate()
|
||||
let factory = RCTReactNativeFactory(delegate: delegate)
|
||||
delegate.dependencyProvider = RCTAppDependencyProvider()
|
||||
|
||||
reactNativeDelegate = delegate
|
||||
reactNativeFactory = factory
|
||||
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
|
||||
factory.startReactNative(
|
||||
withModuleName: "Pre-Trip_Inspections",
|
||||
in: window,
|
||||
launchOptions: launchOptions
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
|
||||
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
||||
self.bundleURL()
|
||||
}
|
||||
|
||||
override func bundleURL() -> URL? {
|
||||
#if DEBUG
|
||||
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
|
||||
#else
|
||||
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
6
rn/app/ios/app/Images.xcassets/Contents.json
Normal file
6
rn/app/ios/app/Images.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
54
rn/app/ios/app/Info.plist
Normal file
54
rn/app/ios/app/Info.plist
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Парус© ПО</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Парус© Предрейсовые осмотры</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
47
rn/app/ios/app/LaunchScreen.storyboard
Normal file
47
rn/app/ios/app/LaunchScreen.storyboard
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="app" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
|
||||
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="MN2-I3-ftu">
|
||||
<rect key="frame" x="0.0" y="626" width="375" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="MN2-I3-ftu" secondAttribute="bottom" constant="20" id="OZV-Vh-mqD"/>
|
||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
|
||||
<constraint firstItem="MN2-I3-ftu" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="akx-eg-2ui"/>
|
||||
<constraint firstItem="MN2-I3-ftu" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" id="i1E-0Y-4RG"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="52.173913043478265" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
37
rn/app/ios/app/PrivacyInfo.xcprivacy
Normal file
37
rn/app/ios/app/PrivacyInfo.xcprivacy
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
3
rn/app/jest.config.js
Normal file
3
rn/app/jest.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
preset: 'react-native',
|
||||
};
|
||||
11
rn/app/metro.config.js
Normal file
11
rn/app/metro.config.js
Normal file
@ -0,0 +1,11 @@
|
||||
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
* https://reactnative.dev/docs/metro
|
||||
*
|
||||
* @type {import('@react-native/metro-config').MetroConfig}
|
||||
*/
|
||||
const config = {};
|
||||
|
||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
||||
44
rn/app/package.json
Normal file
44
rn/app/package.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios",
|
||||
"lint": "eslint .",
|
||||
"start": "react-native start",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native/new-app-screen": "^0.83.1",
|
||||
"react": "^19.2.0",
|
||||
"react-native": "^0.83.1",
|
||||
"react-native-quick-sqlite": "^8.2.7",
|
||||
"react-native-safe-area-context": "^5.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/eslint-parser": "^7.28.6",
|
||||
"@babel/plugin-syntax-jsx": "^7.28.6",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/runtime": "^7.25.0",
|
||||
"@react-native-community/cli": "^20.0.0",
|
||||
"@react-native-community/cli-platform-android": "^20.0.0",
|
||||
"@react-native-community/cli-platform-ios": "^20.0.0",
|
||||
"@react-native/babel-preset": "^0.83.1",
|
||||
"@react-native/eslint-config": "^0.83.1",
|
||||
"@react-native/metro-config": "^0.83.1",
|
||||
"@react-native/typescript-config": "^0.83.1",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-test-renderer": "^19.1.0",
|
||||
"eslint": "^8.19.0",
|
||||
"jest": "^29.6.3",
|
||||
"prettier": "^2.8.8",
|
||||
"react-test-renderer": "^19.2.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
}
|
||||
47
rn/app/src/components/common/AdaptiveView.js
Normal file
47
rn/app/src/components/common/AdaptiveView.js
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Адаптивный контейнер для всех экранов
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View } = require('react-native'); //Базовые компоненты
|
||||
const { useSafeAreaInsets } = require('react-native-safe-area-context'); //Отступы безопасной области
|
||||
const styles = require('../../styles/common/AdaptiveView.styles'); //Стили адаптивного вида
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Адаптивный контейнер для экранов
|
||||
function AdaptiveView({ children, style, padding = true, safeArea = true, ...restProps }) {
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
//Определяем стиль контейнера с учетом safe area
|
||||
const containerStyle = [
|
||||
styles.container,
|
||||
padding && styles.padding,
|
||||
safeArea && {
|
||||
paddingTop: insets.top,
|
||||
paddingBottom: insets.bottom,
|
||||
paddingLeft: insets.left,
|
||||
paddingRight: insets.right
|
||||
},
|
||||
style
|
||||
];
|
||||
|
||||
return (
|
||||
<View style={containerStyle} {...restProps}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AdaptiveView;
|
||||
42
rn/app/src/components/common/AppButton.js
Normal file
42
rn/app/src/components/common/AppButton.js
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Общий компонент кнопки
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { Pressable, View } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('./AppText'); //Общий текстовый компонент
|
||||
const styles = require('../../styles/common/AppButton.styles'); //Стили кнопки
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Общая кнопка приложения
|
||||
function AppButton({ title, onPress, disabled = false, style, textStyle }) {
|
||||
const handlePress = React.useCallback(() => {
|
||||
if (!disabled && typeof onPress === 'function') onPress();
|
||||
}, [disabled, onPress]);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={handlePress}
|
||||
disabled={disabled}
|
||||
style={({ pressed }) => [styles.base, disabled && styles.disabled, pressed && !disabled && styles.pressed, style]}
|
||||
>
|
||||
<View style={styles.content}>
|
||||
<AppText style={[styles.text, textStyle]}>{title}</AppText>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppButton;
|
||||
80
rn/app/src/components/common/AppInput.js
Normal file
80
rn/app/src/components/common/AppInput.js
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Адаптивный компонент ввода
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { TextInput, View } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('./AppText'); //Общий текстовый компонент
|
||||
const styles = require('../../styles/common/AppInput.styles'); //Стили ввода
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Адаптивный компонент ввода
|
||||
function AppInput({
|
||||
label,
|
||||
value,
|
||||
onChangeText,
|
||||
placeholder,
|
||||
secureTextEntry = false,
|
||||
keyboardType = 'default',
|
||||
autoCapitalize = 'none',
|
||||
error,
|
||||
helperText,
|
||||
disabled = false,
|
||||
style,
|
||||
inputStyle,
|
||||
labelStyle,
|
||||
...restProps
|
||||
}) {
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
|
||||
const handleFocus = () => setIsFocused(true);
|
||||
const handleBlur = () => setIsFocused(false);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, style]}>
|
||||
{label && (
|
||||
<AppText style={[styles.label, labelStyle]} variant="caption" weight="medium">
|
||||
{label}
|
||||
</AppText>
|
||||
)}
|
||||
|
||||
<TextInput
|
||||
style={[styles.input, isFocused && styles.inputFocused, error && styles.inputError, disabled && styles.inputDisabled, inputStyle]}
|
||||
value={value}
|
||||
onChangeText={onChangeText}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={styles.placeholder.color}
|
||||
secureTextEntry={secureTextEntry}
|
||||
keyboardType={keyboardType}
|
||||
autoCapitalize={autoCapitalize}
|
||||
editable={!disabled}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
accessible={true}
|
||||
accessibilityLabel={label || placeholder}
|
||||
accessibilityRole="text"
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
{(error || helperText) && (
|
||||
<AppText style={[styles.helperText, error && styles.helperTextError]} variant="caption">
|
||||
{error || helperText}
|
||||
</AppText>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppInput;
|
||||
72
rn/app/src/components/common/AppLogo.js
Normal file
72
rn/app/src/components/common/AppLogo.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент иконки/логотипа приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View } = require('react-native'); //Базовые компоненты
|
||||
const styles = require('../../styles/common/AppLogo.styles'); //Стили логотипа
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Иконка/логотип приложения
|
||||
function AppLogo({ size = 'medium', style }) {
|
||||
//Выбор стилей в зависимости от размера
|
||||
const getSizeStyles = React.useCallback(() => {
|
||||
switch (size) {
|
||||
case 'small':
|
||||
return {
|
||||
container: styles.containerSmall,
|
||||
head: styles.headSmall,
|
||||
eye: styles.eyeSmall,
|
||||
antenna: styles.antennaSmall
|
||||
};
|
||||
case 'large':
|
||||
return {
|
||||
container: styles.containerLarge,
|
||||
head: styles.headLarge,
|
||||
eye: styles.eyeLarge,
|
||||
antenna: styles.antennaLarge
|
||||
};
|
||||
default:
|
||||
return {
|
||||
container: styles.containerMedium,
|
||||
head: styles.headMedium,
|
||||
eye: styles.eyeMedium,
|
||||
antenna: styles.antennaMedium
|
||||
};
|
||||
}
|
||||
}, [size]);
|
||||
|
||||
const sizeStyles = getSizeStyles();
|
||||
|
||||
return (
|
||||
<View style={[styles.container, sizeStyles.container, style]}>
|
||||
<View style={styles.robotContainer}>
|
||||
<View style={styles.antennasContainer}>
|
||||
<View style={[styles.antenna, styles.antennaLeft, sizeStyles.antenna]} />
|
||||
<View style={[styles.antenna, styles.antennaRight, sizeStyles.antenna]} />
|
||||
</View>
|
||||
|
||||
<View style={[styles.head, sizeStyles.head]}>
|
||||
<View style={styles.eyesContainer}>
|
||||
<View style={[styles.eye, sizeStyles.eye]} />
|
||||
<View style={[styles.eye, sizeStyles.eye]} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppLogo;
|
||||
140
rn/app/src/components/common/AppMessage.js
Normal file
140
rn/app/src/components/common/AppMessage.js
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Общий компонент сообщения приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require("react"); //React и хуки
|
||||
const { Modal, View, Text, Pressable } = require("react-native"); //Базовые компоненты
|
||||
const styles = require("../../styles/common/AppMessage.styles"); //Стили сообщения
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Типы сообщений
|
||||
const APP_MESSAGE_VARIANT = {
|
||||
INFO: "INFO",
|
||||
WARN: "WARN",
|
||||
ERR: "ERR",
|
||||
SUCCESS: "SUCCESS"
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Кнопка сообщения
|
||||
function AppMessageButton({ title, onPress, onDismiss, buttonStyle, textStyle }) {
|
||||
//Обработчик нажатия - вызывает onPress и закрывает диалог
|
||||
const handlePress = React.useCallback(() => {
|
||||
//Сначала закрываем диалог
|
||||
if (typeof onDismiss === "function") {
|
||||
onDismiss();
|
||||
}
|
||||
|
||||
//Затем выполняем действие кнопки
|
||||
if (typeof onPress === "function") {
|
||||
onPress();
|
||||
}
|
||||
}, [onPress, onDismiss]);
|
||||
|
||||
return (
|
||||
<Pressable style={({ pressed }) => [styles.buttonBase, pressed && styles.buttonPressed, buttonStyle]} onPress={handlePress}>
|
||||
<Text style={[styles.buttonText, textStyle]}>{title}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
//Сообщение приложения
|
||||
function AppMessage({
|
||||
visible,
|
||||
variant = APP_MESSAGE_VARIANT.INFO,
|
||||
title,
|
||||
message,
|
||||
//Кнопки: массив объектов { id, title, onPress, buttonStyle, textStyle }
|
||||
buttons,
|
||||
//Обработчик закрытия по "крестику" или по системному Back на Android
|
||||
onRequestClose,
|
||||
//Кастомизация стилей контейнера и содержимого
|
||||
containerStyle,
|
||||
contentStyle,
|
||||
titleStyle,
|
||||
messageStyle,
|
||||
headerStyle
|
||||
}) {
|
||||
//Флаг наличия кнопок
|
||||
const hasButtons = Array.isArray(buttons) && buttons.length > 0;
|
||||
|
||||
//Подбор варианта оформления по типу сообщения
|
||||
const containerVariantStyle =
|
||||
variant === APP_MESSAGE_VARIANT.ERR
|
||||
? styles.containerError
|
||||
: variant === APP_MESSAGE_VARIANT.WARN
|
||||
? styles.containerWarn
|
||||
: variant === APP_MESSAGE_VARIANT.SUCCESS
|
||||
? styles.containerSuccess
|
||||
: styles.containerInfo;
|
||||
|
||||
//Обработчик закрытия
|
||||
const handleClose = React.useCallback(() => {
|
||||
if (typeof onRequestClose === "function") onRequestClose();
|
||||
}, [onRequestClose]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={!!visible}
|
||||
onRequestClose={handleClose}
|
||||
>
|
||||
<View style={styles.backdrop}>
|
||||
<View style={[styles.container, containerVariantStyle, containerStyle]}>
|
||||
<View style={[styles.header, headerStyle]}>
|
||||
<Text style={[styles.title, titleStyle]}>
|
||||
{title || ""}
|
||||
</Text>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Закрыть сообщение"
|
||||
onPress={handleClose}
|
||||
style={styles.closeButton}
|
||||
>
|
||||
<Text style={styles.closeButtonText}>×</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
<View style={[styles.content, contentStyle]}>
|
||||
<Text style={[styles.message, messageStyle]}>{message}</Text>
|
||||
</View>
|
||||
{hasButtons ? (
|
||||
<View style={styles.buttonsRow}>
|
||||
{buttons.map(btn => (
|
||||
<AppMessageButton
|
||||
key={btn.id || btn.title}
|
||||
title={btn.title}
|
||||
onPress={btn.onPress}
|
||||
onDismiss={handleClose}
|
||||
buttonStyle={btn.buttonStyle}
|
||||
textStyle={btn.textStyle}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
AppMessage,
|
||||
APP_MESSAGE_VARIANT
|
||||
};
|
||||
|
||||
31
rn/app/src/components/common/AppText.js
Normal file
31
rn/app/src/components/common/AppText.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Общий компонент текста
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { Text } = require('react-native'); //Базовый компонент текста
|
||||
const styles = require('../../styles/common/AppText.styles'); //Стили текста
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Общий текстовый компонент приложения
|
||||
function AppText({ style, children, ...restProps }) {
|
||||
return (
|
||||
<Text style={[styles.text, style]} {...restProps}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppText;
|
||||
41
rn/app/src/components/common/Backdrop.js
Normal file
41
rn/app/src/components/common/Backdrop.js
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент заднего фона (оверлей)
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { Pressable } = require('react-native'); //Базовые компоненты
|
||||
const styles = require('../../styles/common/Backdrop.styles'); //Стили заднего фона
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Задний фон для модальных окон и меню
|
||||
function Backdrop({ visible, onPress, style, children }) {
|
||||
const handlePress = React.useCallback(() => {
|
||||
if (typeof onPress === 'function') {
|
||||
onPress();
|
||||
}
|
||||
}, [onPress]);
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable style={[styles.backdrop, style]} onPress={handlePress}>
|
||||
{children}
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = Backdrop;
|
||||
69
rn/app/src/components/common/CopyButton.js
Normal file
69
rn/app/src/components/common/CopyButton.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент кнопки копирования в буфер обмена
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View, Pressable } = require('react-native'); //Базовые компоненты
|
||||
const { copyToClipboard } = require('../../utils/clipboard'); //Утилита буфера обмена
|
||||
const styles = require('../../styles/common/CopyButton.styles'); //Стили кнопки копирования
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Иконка копирования (два прямоугольника)
|
||||
function CopyIcon() {
|
||||
return (
|
||||
<View style={styles.iconContainer}>
|
||||
<View style={styles.iconBack} />
|
||||
<View style={styles.iconFront} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//Кнопка копирования в буфер обмена
|
||||
function CopyButton({ value, onCopy, onError, disabled = false, style }) {
|
||||
//Обработчик нажатия
|
||||
const handlePress = React.useCallback(async () => {
|
||||
if (disabled || !value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await copyToClipboard(String(value));
|
||||
|
||||
if (typeof onCopy === 'function') {
|
||||
onCopy(value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка копирования в буфер:', error);
|
||||
|
||||
if (typeof onError === 'function') {
|
||||
onError(error);
|
||||
}
|
||||
}
|
||||
}, [value, disabled, onCopy, onError]);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Копировать в буфер обмена"
|
||||
style={({ pressed }) => [styles.button, pressed && !disabled && styles.buttonPressed, disabled && styles.buttonDisabled, style]}
|
||||
onPress={handlePress}
|
||||
disabled={disabled}
|
||||
>
|
||||
<CopyIcon />
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = CopyButton;
|
||||
156
rn/app/src/components/common/InputDialog.js
Normal file
156
rn/app/src/components/common/InputDialog.js
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент модального окна с полем ввода
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { Modal, View, TextInput, Pressable } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('./AppText'); //Общий текстовый компонент
|
||||
const AppButton = require('./AppButton'); //Кнопка
|
||||
const styles = require('../../styles/common/InputDialog.styles'); //Стили диалога
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Модальное окно с полем ввода
|
||||
function InputDialog({
|
||||
visible,
|
||||
title = 'Ввод данных',
|
||||
label,
|
||||
value = '',
|
||||
placeholder,
|
||||
keyboardType = 'default',
|
||||
autoCapitalize = 'none',
|
||||
confirmText = 'Сохранить',
|
||||
cancelText = 'Отмена',
|
||||
onConfirm,
|
||||
onCancel,
|
||||
validator,
|
||||
errorMessage
|
||||
}) {
|
||||
//Локальное значение для редактирования
|
||||
const [inputValue, setInputValue] = React.useState(value);
|
||||
const [error, setError] = React.useState('');
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
|
||||
//Сброс значения при открытии диалога
|
||||
React.useEffect(() => {
|
||||
if (visible) {
|
||||
setInputValue(value);
|
||||
setError('');
|
||||
}
|
||||
}, [visible, value]);
|
||||
|
||||
//Обработчик фокуса
|
||||
const handleFocus = React.useCallback(() => {
|
||||
setIsFocused(true);
|
||||
}, []);
|
||||
|
||||
//Обработчик потери фокуса
|
||||
const handleBlur = React.useCallback(() => {
|
||||
setIsFocused(false);
|
||||
}, []);
|
||||
|
||||
//Обработчик изменения текста
|
||||
const handleChangeText = React.useCallback(text => {
|
||||
setInputValue(text);
|
||||
setError('');
|
||||
}, []);
|
||||
|
||||
//Валидация введённого значения
|
||||
const validateInput = React.useCallback(() => {
|
||||
if (typeof validator === 'function') {
|
||||
const validationResult = validator(inputValue);
|
||||
if (validationResult !== true) {
|
||||
setError(validationResult || errorMessage || 'Некорректное значение');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, [inputValue, validator, errorMessage]);
|
||||
|
||||
//Обработчик подтверждения
|
||||
const handleConfirm = React.useCallback(() => {
|
||||
if (!validateInput()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof onConfirm === 'function') {
|
||||
onConfirm(inputValue.trim());
|
||||
}
|
||||
}, [inputValue, validateInput, onConfirm]);
|
||||
|
||||
//Обработчик отмены
|
||||
const handleCancel = React.useCallback(() => {
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
}, [onCancel]);
|
||||
|
||||
//Обработчик закрытия по кнопке "Назад" (Android)
|
||||
const handleRequestClose = React.useCallback(() => {
|
||||
handleCancel();
|
||||
}, [handleCancel]);
|
||||
|
||||
return (
|
||||
<Modal visible={visible} transparent={true} animationType="fade" statusBarTranslucent={true} onRequestClose={handleRequestClose}>
|
||||
<View style={styles.backdrop}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<AppText style={styles.title}>{title}</AppText>
|
||||
<Pressable accessibilityRole="button" accessibilityLabel="Закрыть" onPress={handleCancel} style={styles.closeButton}>
|
||||
<AppText style={styles.closeButtonText}>×</AppText>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<View style={styles.content}>
|
||||
{label ? (
|
||||
<AppText style={styles.label} variant="caption" weight="medium">
|
||||
{label}
|
||||
</AppText>
|
||||
) : null}
|
||||
|
||||
<TextInput
|
||||
style={[styles.input, isFocused && styles.inputFocused, error && styles.inputError]}
|
||||
value={inputValue}
|
||||
onChangeText={handleChangeText}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={styles.placeholder.color}
|
||||
keyboardType={keyboardType}
|
||||
autoCapitalize={autoCapitalize}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
autoFocus={true}
|
||||
selectTextOnFocus={true}
|
||||
accessible={true}
|
||||
accessibilityLabel={label || placeholder}
|
||||
accessibilityRole="text"
|
||||
/>
|
||||
|
||||
{error ? (
|
||||
<AppText style={styles.errorText} variant="caption">
|
||||
{error}
|
||||
</AppText>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<View style={styles.buttonsRow}>
|
||||
<AppButton title={cancelText} onPress={handleCancel} style={styles.cancelButton} textStyle={styles.cancelButtonText} />
|
||||
<AppButton title={confirmText} onPress={handleConfirm} style={styles.confirmButton} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = InputDialog;
|
||||
37
rn/app/src/components/inspections/InspectionItem.js
Normal file
37
rn/app/src/components/inspections/InspectionItem.js
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Элемент списка предрейсовых осмотров
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require("react"); //React
|
||||
const { View } = require("react-native"); //Базовые компоненты
|
||||
const AppText = require("../common/AppText"); //Общий текстовый компонент
|
||||
const styles = require("../../styles/inspections/InspectionItem.styles"); //Стили элемента
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Элемент списка осмотров
|
||||
function InspectionItem({ item }) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<AppText style={styles.title}>{item.title}</AppText>
|
||||
<View style={styles.metaRow}>
|
||||
<AppText style={styles.meta}>Статус: {item.status}</AppText>
|
||||
<AppText style={styles.meta}>Создан: {item.createdAt}</AppText>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = InspectionItem;
|
||||
|
||||
68
rn/app/src/components/inspections/InspectionList.js
Normal file
68
rn/app/src/components/inspections/InspectionList.js
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Список предрейсовых осмотров
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { ActivityIndicator, FlatList, RefreshControl, View } = require('react-native'); //Базовые компоненты списка
|
||||
const AppText = require('../common/AppText'); //Общий текстовый компонент
|
||||
const AppButton = require('../common/AppButton'); //Общая кнопка
|
||||
const InspectionItem = require('./InspectionItem'); //Элемент списка
|
||||
const styles = require('../../styles/inspections/InspectionList.styles'); //Стили списка
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Список осмотров
|
||||
function InspectionList({ inspections, isLoading, error, onRefresh }) {
|
||||
const hasData = Array.isArray(inspections) && inspections.length > 0;
|
||||
|
||||
const renderItem = React.useCallback(({ item }) => <InspectionItem item={item} />, []);
|
||||
|
||||
const keyExtractor = React.useCallback(item => item.id, []);
|
||||
|
||||
const handleRefresh = React.useCallback(() => {
|
||||
if (typeof onRefresh === 'function') onRefresh();
|
||||
}, [onRefresh]);
|
||||
|
||||
if (!hasData && isLoading) {
|
||||
return (
|
||||
<View style={styles.centerContainer}>
|
||||
<ActivityIndicator size="small" color="#2563EB" />
|
||||
<AppText style={styles.centerText}>Загружаем данные...</AppText>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasData && !isLoading) {
|
||||
return (
|
||||
<View style={styles.centerContainer}>
|
||||
<AppText style={styles.centerText}>Нет данных предрейсовых осмотров</AppText>
|
||||
{error ? <AppText style={styles.errorText}>{error}</AppText> : null}
|
||||
<View style={styles.centerButton}>
|
||||
<AppButton title="Обновить" onPress={handleRefresh} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={inspections}
|
||||
keyExtractor={keyExtractor}
|
||||
renderItem={renderItem}
|
||||
refreshControl={<RefreshControl refreshing={!!isLoading} onRefresh={handleRefresh} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = InspectionList;
|
||||
88
rn/app/src/components/layout/AppErrorBoundary.js
Normal file
88
rn/app/src/components/layout/AppErrorBoundary.js
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Обёртка приложения для обработки ошибок
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('../common/AppText'); //Текстовый компонент
|
||||
const AppButton = require('../common/AppButton'); //Кнопка
|
||||
const styles = require('../../styles/layout/AppErrorBoundary.styles'); //Стили компонента
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Компонент страницы ошибки
|
||||
function AppErrorPage({ error, onReload }) {
|
||||
const message = error && error.message ? String(error.message) : 'Произошла непредвиденная ошибка приложения.';
|
||||
|
||||
const handleReload = () => {
|
||||
if (typeof onReload === 'function') {
|
||||
onReload();
|
||||
return;
|
||||
}
|
||||
//Попытка перезагрузки для Web
|
||||
if (typeof window !== 'undefined' && window.location && typeof window.location.reload === 'function') {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.card}>
|
||||
<AppText style={styles.title}>Ошибка приложения</AppText>
|
||||
<AppText style={styles.message}>{message}</AppText>
|
||||
<View style={styles.buttonRow}>
|
||||
<AppButton title="Перезапустить" onPress={handleReload} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//Классический Error Boundary
|
||||
class AppErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
hasError: false,
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
return {
|
||||
hasError: true,
|
||||
error
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
//TODO: логгер
|
||||
}
|
||||
|
||||
handleReset = () => {
|
||||
this.setState({
|
||||
hasError: false,
|
||||
error: null
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return <AppErrorPage error={this.state.error} onReload={this.handleReset} />;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppErrorBoundary;
|
||||
185
rn/app/src/components/layout/AppHeader.js
Normal file
185
rn/app/src/components/layout/AppHeader.js
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент заголовка приложения с меню
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View, Pressable } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('../common/AppText'); //Общий текстовый компонент
|
||||
const AppLogo = require('../common/AppLogo'); //Логотип приложения
|
||||
const { useAppModeContext } = require('./AppModeProvider'); //Контекст режима работы
|
||||
const { useAppNavigationContext } = require('./AppNavigationProvider'); //Контекст навигации
|
||||
const { useAppMessagingContext } = require('./AppMessagingProvider'); //Контекст сообщений
|
||||
const { getModeLabel, getModeDescription } = require('../../utils/appInfo'); //Утилиты информации
|
||||
const styles = require('../../styles/layout/AppHeader.styles'); //Стили заголовка
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Индикатор режима работы
|
||||
function ModeIndicator({ mode, onPress }) {
|
||||
//Получение конфигурации стилей для режима
|
||||
const getModeStyleConfig = React.useCallback(() => {
|
||||
switch (mode) {
|
||||
case 'ONLINE':
|
||||
return {
|
||||
color: styles.modeOnline,
|
||||
textColor: styles.modeTextOnline
|
||||
};
|
||||
case 'OFFLINE':
|
||||
return {
|
||||
color: styles.modeOffline,
|
||||
textColor: styles.modeTextOffline
|
||||
};
|
||||
case 'NOT_CONNECTED':
|
||||
return {
|
||||
color: styles.modeNotConnected,
|
||||
textColor: styles.modeTextNotConnected
|
||||
};
|
||||
default:
|
||||
return {
|
||||
color: styles.modeUnknown,
|
||||
textColor: styles.modeTextUnknown
|
||||
};
|
||||
}
|
||||
}, [mode]);
|
||||
|
||||
const styleConfig = getModeStyleConfig();
|
||||
const label = getModeLabel(mode);
|
||||
|
||||
return (
|
||||
<Pressable style={({ pressed }) => [styles.modeContainer, styleConfig.color, pressed && styles.modePressed]} onPress={onPress}>
|
||||
<AppText style={[styles.modeText, styleConfig.textColor]}>{label}</AppText>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
//Иконка стрелки назад
|
||||
function BackArrowIcon() {
|
||||
return (
|
||||
<View style={styles.backArrowIcon}>
|
||||
<View style={styles.backArrowLineTop} />
|
||||
<View style={styles.backArrowLineBottom} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//Кнопка назад
|
||||
function BackButton({ onPress }) {
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Назад"
|
||||
style={({ pressed }) => [styles.backButton, pressed && styles.backButtonPressed]}
|
||||
onPress={onPress}
|
||||
>
|
||||
<BackArrowIcon />
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
//Заголовок приложения
|
||||
function AppHeader({
|
||||
title,
|
||||
subtitle,
|
||||
showMenuButton = true,
|
||||
onMenuPress,
|
||||
showModeIndicator = true,
|
||||
showBackButton = false,
|
||||
onBackPress
|
||||
}) {
|
||||
const { mode } = useAppModeContext();
|
||||
const { currentScreen, SCREENS } = useAppNavigationContext();
|
||||
const { showInfo } = useAppMessagingContext();
|
||||
|
||||
//Получение заголовка экрана
|
||||
const getTitle = React.useCallback(() => {
|
||||
if (title) return title;
|
||||
|
||||
switch (currentScreen) {
|
||||
case SCREENS.MAIN:
|
||||
return 'Парус© Предрейсовые осмотры';
|
||||
case SCREENS.SETTINGS:
|
||||
return 'Настройки';
|
||||
default:
|
||||
return 'Парус© Предрейсовые осмотры';
|
||||
}
|
||||
}, [title, currentScreen, SCREENS.MAIN, SCREENS.SETTINGS]);
|
||||
|
||||
//Получение подзаголовка экрана
|
||||
const getSubtitle = React.useCallback(() => {
|
||||
if (subtitle) return subtitle;
|
||||
|
||||
switch (currentScreen) {
|
||||
case SCREENS.MAIN:
|
||||
return mode === 'NOT_CONNECTED' ? 'Требуется настройка сервера' : 'Демонстрационный экран';
|
||||
case SCREENS.SETTINGS:
|
||||
return 'Конфигурация приложения';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}, [subtitle, currentScreen, mode, SCREENS.MAIN, SCREENS.SETTINGS]);
|
||||
|
||||
//Обработчик нажатия на индикатор режима (универсальный для всех экранов)
|
||||
const handleModeIndicatorPress = React.useCallback(() => {
|
||||
const modeLabel = getModeLabel(mode);
|
||||
const modeDescription = getModeDescription(mode);
|
||||
|
||||
showInfo(`Текущий режим: ${modeLabel}`, {
|
||||
message: modeDescription
|
||||
});
|
||||
}, [mode, showInfo]);
|
||||
|
||||
//Отрисовка левой части шапки (логотип или кнопка назад)
|
||||
const renderLeftSection = () => {
|
||||
if (showBackButton) {
|
||||
return <BackButton onPress={onBackPress} />;
|
||||
}
|
||||
|
||||
return <AppLogo size="small" style={styles.logo} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.content}>
|
||||
{renderLeftSection()}
|
||||
|
||||
<View style={styles.titleContainer}>
|
||||
<AppText style={styles.title} numberOfLines={1}>
|
||||
{getTitle()}
|
||||
</AppText>
|
||||
{getSubtitle() ? (
|
||||
<AppText style={styles.subtitle} numberOfLines={1}>
|
||||
{getSubtitle()}
|
||||
</AppText>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<View style={styles.controls}>
|
||||
{showModeIndicator ? <ModeIndicator mode={mode} onPress={handleModeIndicatorPress} /> : null}
|
||||
|
||||
{showMenuButton ? (
|
||||
<Pressable style={({ pressed }) => [styles.menuButton, pressed && styles.menuButtonPressed]} onPress={onMenuPress}>
|
||||
<View style={styles.menuButtonIcon}>
|
||||
<View style={styles.menuButtonIconLine} />
|
||||
<View style={styles.menuButtonIconLine} />
|
||||
<View style={styles.menuButtonIconLine} />
|
||||
</View>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppHeader;
|
||||
77
rn/app/src/components/layout/AppLocalDbProvider.js
Normal file
77
rn/app/src/components/layout/AppLocalDbProvider.js
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Провайдер контекста работы с локальной базой данных
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const useLocalDb = require('../../hooks/useLocalDb'); //Хук локальной БД
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Контекст локальной БД
|
||||
const AppLocalDbContext = React.createContext(null);
|
||||
|
||||
//Провайдер локальной БД
|
||||
function AppLocalDbProvider({ children }) {
|
||||
const api = useLocalDb();
|
||||
|
||||
//Мемоизация значения контекста с перечислением отдельных свойств
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
isDbReady: api.isDbReady,
|
||||
inspections: api.inspections,
|
||||
error: api.error,
|
||||
loadInspections: api.loadInspections,
|
||||
saveInspection: api.saveInspection,
|
||||
getSetting: api.getSetting,
|
||||
setSetting: api.setSetting,
|
||||
deleteSetting: api.deleteSetting,
|
||||
getAllSettings: api.getAllSettings,
|
||||
clearSettings: api.clearSettings,
|
||||
clearInspections: api.clearInspections,
|
||||
vacuum: api.vacuum,
|
||||
checkTableExists: api.checkTableExists
|
||||
}),
|
||||
[
|
||||
api.isDbReady,
|
||||
api.inspections,
|
||||
api.error,
|
||||
api.loadInspections,
|
||||
api.saveInspection,
|
||||
api.getSetting,
|
||||
api.setSetting,
|
||||
api.deleteSetting,
|
||||
api.getAllSettings,
|
||||
api.clearSettings,
|
||||
api.clearInspections,
|
||||
api.vacuum,
|
||||
api.checkTableExists
|
||||
]
|
||||
);
|
||||
|
||||
return <AppLocalDbContext.Provider value={value}>{children}</AppLocalDbContext.Provider>;
|
||||
}
|
||||
|
||||
//Хук доступа к контексту локальной БД
|
||||
function useAppLocalDbContext() {
|
||||
const ctx = React.useContext(AppLocalDbContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppLocalDbContext должен использоваться внутри AppLocalDbProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
AppLocalDbProvider,
|
||||
useAppLocalDbContext
|
||||
};
|
||||
93
rn/app/src/components/layout/AppMessagingProvider.js
Normal file
93
rn/app/src/components/layout/AppMessagingProvider.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Провайдер контекста сообщений приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const { AppMessage } = require('../common/AppMessage'); //Компонент сообщения
|
||||
const useAppMessaging = require('../../hooks/useAppMessaging'); //Хук сообщений
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Контекст сообщений приложения
|
||||
const AppMessagingContext = React.createContext(null);
|
||||
|
||||
//Провайдер сообщений приложения
|
||||
function AppMessagingProvider({ children }) {
|
||||
const messagingApi = useAppMessaging();
|
||||
|
||||
//Деструктурируем необходимые методы и состояние
|
||||
const { hideMessage, state } = messagingApi;
|
||||
|
||||
//Обработчик закрытия окна "крестиком" / Back
|
||||
const handleRequestClose = React.useCallback(() => {
|
||||
hideMessage();
|
||||
}, [hideMessage]);
|
||||
|
||||
//Мемоизация значения контекста с перечислением отдельных свойств
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
MSG_TYPE: messagingApi.MSG_TYPE,
|
||||
state: messagingApi.state,
|
||||
showMessage: messagingApi.showMessage,
|
||||
showError: messagingApi.showError,
|
||||
showInfo: messagingApi.showInfo,
|
||||
showWarn: messagingApi.showWarn,
|
||||
showSuccess: messagingApi.showSuccess,
|
||||
hideMessage: messagingApi.hideMessage
|
||||
}),
|
||||
[
|
||||
messagingApi.MSG_TYPE,
|
||||
messagingApi.state,
|
||||
messagingApi.showMessage,
|
||||
messagingApi.showError,
|
||||
messagingApi.showInfo,
|
||||
messagingApi.showWarn,
|
||||
messagingApi.showSuccess,
|
||||
messagingApi.hideMessage
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<AppMessagingContext.Provider value={value}>
|
||||
{children}
|
||||
<AppMessage
|
||||
visible={state.visible}
|
||||
variant={state.variant}
|
||||
title={state.title}
|
||||
message={state.message}
|
||||
buttons={state.buttons}
|
||||
onRequestClose={handleRequestClose}
|
||||
containerStyle={state.containerStyle}
|
||||
contentStyle={state.contentStyle}
|
||||
titleStyle={state.titleStyle}
|
||||
messageStyle={state.messageStyle}
|
||||
headerStyle={state.headerStyle}
|
||||
/>
|
||||
</AppMessagingContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
//Хук доступа к контексту сообщений приложения
|
||||
function useAppMessagingContext() {
|
||||
const ctx = React.useContext(AppMessagingContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppMessagingContext должен использоваться внутри AppMessagingProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
AppMessagingProvider,
|
||||
useAppMessagingContext
|
||||
};
|
||||
69
rn/app/src/components/layout/AppModeProvider.js
Normal file
69
rn/app/src/components/layout/AppModeProvider.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Провайдер контекста режима работы приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const useAppMode = require('../../hooks/useAppMode'); //Хук режима работы
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Контекст режима работы приложения
|
||||
const AppModeContext = React.createContext(null);
|
||||
|
||||
//Провайдер режима работы приложения
|
||||
function AppModeProvider({ children }) {
|
||||
const modeApi = useAppMode();
|
||||
|
||||
//Мемоизация значения контекста с перечислением отдельных свойств
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
APP_MODE: modeApi.APP_MODE,
|
||||
mode: modeApi.mode,
|
||||
setMode: modeApi.setMode,
|
||||
setOnline: modeApi.setOnline,
|
||||
setOffline: modeApi.setOffline,
|
||||
setNotConnected: modeApi.setNotConnected,
|
||||
isOnline: modeApi.isOnline,
|
||||
isOffline: modeApi.isOffline,
|
||||
isNotConnected: modeApi.isNotConnected
|
||||
}),
|
||||
[
|
||||
modeApi.APP_MODE,
|
||||
modeApi.mode,
|
||||
modeApi.setMode,
|
||||
modeApi.setOnline,
|
||||
modeApi.setOffline,
|
||||
modeApi.setNotConnected,
|
||||
modeApi.isOnline,
|
||||
modeApi.isOffline,
|
||||
modeApi.isNotConnected
|
||||
]
|
||||
);
|
||||
|
||||
return <AppModeContext.Provider value={value}>{children}</AppModeContext.Provider>;
|
||||
}
|
||||
|
||||
//Хук доступа к контексту режима работы приложения
|
||||
function useAppModeContext() {
|
||||
const ctx = React.useContext(AppModeContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppModeContext должен использоваться внутри AppModeProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
AppModeProvider,
|
||||
useAppModeContext
|
||||
};
|
||||
84
rn/app/src/components/layout/AppNavigationProvider.js
Normal file
84
rn/app/src/components/layout/AppNavigationProvider.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Провайдер контекста навигации приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const useAppNavigation = require('../../hooks/useAppNavigation'); //Хук навигации
|
||||
const useHardwareBackPress = require('../../hooks/useHardwareBackPress'); //Хук кнопки "Назад"
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Контекст навигации приложения
|
||||
const AppNavigationContext = React.createContext(null);
|
||||
|
||||
//Провайдер навигации приложения
|
||||
function AppNavigationProvider({ children }) {
|
||||
const navigationApi = useAppNavigation();
|
||||
|
||||
//Деструктурируем для использования в useCallback
|
||||
const { canGoBack, goBack } = navigationApi;
|
||||
|
||||
//Обработчик аппаратной кнопки "Назад" (Android)
|
||||
const handleHardwareBackPress = React.useCallback(() => {
|
||||
//Если можно вернуться назад - возвращаемся
|
||||
if (canGoBack) {
|
||||
goBack();
|
||||
return true; //Предотвращаем закрытие приложения
|
||||
}
|
||||
|
||||
//Если нельзя - позволяем системе обработать (закрыть приложение)
|
||||
return false;
|
||||
}, [canGoBack, goBack]);
|
||||
|
||||
//Подключаем обработчик кнопки "Назад"
|
||||
useHardwareBackPress(handleHardwareBackPress, [handleHardwareBackPress]);
|
||||
|
||||
//Мемоизация значения контекста с перечислением отдельных свойств
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
SCREENS: navigationApi.SCREENS,
|
||||
currentScreen: navigationApi.currentScreen,
|
||||
screenParams: navigationApi.screenParams,
|
||||
navigate: navigationApi.navigate,
|
||||
goBack: navigationApi.goBack,
|
||||
reset: navigationApi.reset,
|
||||
canGoBack: navigationApi.canGoBack
|
||||
}),
|
||||
[
|
||||
navigationApi.SCREENS,
|
||||
navigationApi.currentScreen,
|
||||
navigationApi.screenParams,
|
||||
navigationApi.navigate,
|
||||
navigationApi.goBack,
|
||||
navigationApi.reset,
|
||||
navigationApi.canGoBack
|
||||
]
|
||||
);
|
||||
|
||||
return <AppNavigationContext.Provider value={value}>{children}</AppNavigationContext.Provider>;
|
||||
}
|
||||
|
||||
//Хук доступа к контексту навигации приложения
|
||||
function useAppNavigationContext() {
|
||||
const ctx = React.useContext(AppNavigationContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppNavigationContext должен использоваться внутри AppNavigationProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
AppNavigationProvider,
|
||||
useAppNavigationContext
|
||||
};
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Провайдер контекста предметной области "Предрейсовые осмотры"
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const { usePreTripInspections } = require('../../hooks/usePreTripInspections'); //Хук предметной области
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Контекст предрейсовых осмотров
|
||||
const AppPreTripInspectionsContext = React.createContext(null);
|
||||
|
||||
//Провайдер предрейсовых осмотров
|
||||
function AppPreTripInspectionsProvider({ children }) {
|
||||
const inspectionsApi = usePreTripInspections();
|
||||
|
||||
//Мемоизация значения контекста с перечислением отдельных свойств
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
inspections: inspectionsApi.inspections,
|
||||
loadStatus: inspectionsApi.loadStatus,
|
||||
error: inspectionsApi.error,
|
||||
isDbReady: inspectionsApi.isDbReady,
|
||||
refreshInspections: inspectionsApi.refreshInspections,
|
||||
upsertInspection: inspectionsApi.upsertInspection
|
||||
}),
|
||||
[
|
||||
inspectionsApi.inspections,
|
||||
inspectionsApi.loadStatus,
|
||||
inspectionsApi.error,
|
||||
inspectionsApi.isDbReady,
|
||||
inspectionsApi.refreshInspections,
|
||||
inspectionsApi.upsertInspection
|
||||
]
|
||||
);
|
||||
|
||||
return <AppPreTripInspectionsContext.Provider value={value}>{children}</AppPreTripInspectionsContext.Provider>;
|
||||
}
|
||||
|
||||
//Хук доступа к контексту предрейсовых осмотров
|
||||
function useAppPreTripInspectionsContext() {
|
||||
const ctx = React.useContext(AppPreTripInspectionsContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppPreTripInspectionsContext должен использоваться внутри AppPreTripInspectionsProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
AppPreTripInspectionsProvider,
|
||||
useAppPreTripInspectionsContext
|
||||
};
|
||||
35
rn/app/src/components/layout/AppRoot.js
Normal file
35
rn/app/src/components/layout/AppRoot.js
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Корневой layout приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const { useColorScheme } = require('react-native'); //Определение темы устройства
|
||||
const { SafeAreaProvider } = require('react-native-safe-area-context'); //Провайдер безопасной области
|
||||
const AppShell = require('./AppShell'); //Оболочка приложения
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Корневой layout приложения
|
||||
function AppRoot() {
|
||||
const colorScheme = useColorScheme();
|
||||
const isDarkMode = colorScheme === 'dark';
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<AppShell isDarkMode={isDarkMode} />
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppRoot;
|
||||
55
rn/app/src/components/layout/AppServerProvider.js
Normal file
55
rn/app/src/components/layout/AppServerProvider.js
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Провайдер контекста работы с сервером приложений
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const useAppServer = require('../../hooks/useAppServer'); //Хук сервера приложений
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Контекст работы с сервером
|
||||
const AppServerContext = React.createContext(null);
|
||||
|
||||
//Провайдер сервера приложений
|
||||
function AppServerProvider({ children }) {
|
||||
const api = useAppServer();
|
||||
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
executeAction: api.executeAction,
|
||||
abort: api.abort,
|
||||
isRespErr: api.isRespErr,
|
||||
getRespErrMessage: api.getRespErrMessage,
|
||||
RESP_STATUS_OK: api.RESP_STATUS_OK,
|
||||
RESP_STATUS_ERR: api.RESP_STATUS_ERR
|
||||
}),
|
||||
[api.abort, api.executeAction, api.getRespErrMessage, api.isRespErr, api.RESP_STATUS_ERR, api.RESP_STATUS_OK]
|
||||
);
|
||||
|
||||
return <AppServerContext.Provider value={value}>{children}</AppServerContext.Provider>;
|
||||
}
|
||||
|
||||
//Хук доступа к контексту сервера приложений
|
||||
function useAppServerContext() {
|
||||
const ctx = React.useContext(AppServerContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppServerContext должен использоваться внутри AppServerProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
AppServerProvider,
|
||||
useAppServerContext
|
||||
};
|
||||
53
rn/app/src/components/layout/AppShell.js
Normal file
53
rn/app/src/components/layout/AppShell.js
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Оболочка приложения (status bar, общая разметка)
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { StatusBar, Platform } = require('react-native'); //Базовые компоненты
|
||||
const { useAppNavigationContext } = require('./AppNavigationProvider'); //Контекст навигации
|
||||
const MainScreen = require('../../screens/MainScreen'); //Главный экран
|
||||
const SettingsScreen = require('../../screens/SettingsScreen'); //Экран настроек
|
||||
const AdaptiveView = require('../common/AdaptiveView'); //Адаптивный контейнер
|
||||
const styles = require('../../styles/layout/AppShell.styles'); //Стили оболочки
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Оболочка приложения
|
||||
function AppShell({ isDarkMode }) {
|
||||
const { currentScreen, SCREENS } = useAppNavigationContext();
|
||||
|
||||
const renderScreen = React.useCallback(() => {
|
||||
switch (currentScreen) {
|
||||
case SCREENS.MAIN:
|
||||
return <MainScreen />;
|
||||
case SCREENS.SETTINGS:
|
||||
return <SettingsScreen />;
|
||||
default:
|
||||
return <MainScreen />;
|
||||
}
|
||||
}, [currentScreen, SCREENS.MAIN, SCREENS.SETTINGS]);
|
||||
|
||||
//Определяем цвет status bar в зависимости от темы
|
||||
const statusBarStyle = isDarkMode ? 'light-content' : 'dark-content';
|
||||
const statusBarBackground = isDarkMode ? '#0F172A' : '#F8FAFC';
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusBar barStyle={statusBarStyle} backgroundColor={statusBarBackground} translucent={Platform.OS === 'android'} />
|
||||
<AdaptiveView style={styles.container}>{renderScreen()}</AdaptiveView>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = AppShell;
|
||||
32
rn/app/src/components/menu/EmptyMenu.js
Normal file
32
rn/app/src/components/menu/EmptyMenu.js
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент пустого состояния меню
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('../common/AppText'); //Общий текстовый компонент
|
||||
const styles = require('../../styles/menu/EmptyMenu.styles'); //Стили пустого меню
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Пустое состояние меню
|
||||
function EmptyMenu({ message = 'Нет доступных пунктов меню' }) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<AppText style={styles.message}>{message}</AppText>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = EmptyMenu;
|
||||
27
rn/app/src/components/menu/MenuDivider.js
Normal file
27
rn/app/src/components/menu/MenuDivider.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент разделителя в меню
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View } = require('react-native'); //Базовые компоненты
|
||||
const styles = require('../../styles/menu/MenuDivider.styles'); //Стили разделителя
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Разделитель в меню
|
||||
function MenuDivider() {
|
||||
return <View style={styles.divider} />;
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = MenuDivider;
|
||||
52
rn/app/src/components/menu/MenuHeader.js
Normal file
52
rn/app/src/components/menu/MenuHeader.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент заголовка меню
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View, Pressable } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('../common/AppText'); //Общий текстовый компонент
|
||||
const styles = require('../../styles/menu/MenuHeader.styles'); //Стили заголовка меню
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Заголовок меню
|
||||
function MenuHeader({ title, onClose, showCloseButton = true, style }) {
|
||||
const handleClose = React.useCallback(() => {
|
||||
if (typeof onClose === 'function') {
|
||||
onClose();
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<View style={[styles.header, style]}>
|
||||
<AppText style={styles.title} numberOfLines={2}>
|
||||
{title || 'Меню'}
|
||||
</AppText>
|
||||
{showCloseButton ? (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Закрыть меню"
|
||||
onPress={handleClose}
|
||||
style={({ pressed }) => [styles.closeButton, pressed && styles.closeButtonPressed]}
|
||||
>
|
||||
<View style={styles.closeButtonIcon}>
|
||||
<AppText style={styles.closeButtonText}>×</AppText>
|
||||
</View>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = MenuHeader;
|
||||
56
rn/app/src/components/menu/MenuItem.js
Normal file
56
rn/app/src/components/menu/MenuItem.js
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент элемента меню
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { View, Pressable } = require('react-native'); //Базовые компоненты
|
||||
const AppText = require('../common/AppText'); //Общий текстовый компонент
|
||||
const styles = require('../../styles/menu/MenuItem.styles'); //Стили элемента меню
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Элемент меню
|
||||
function MenuItem({ title, icon, onPress, isDestructive = false, disabled = false, style, textStyle }) {
|
||||
const handlePress = React.useCallback(() => {
|
||||
if (!disabled && typeof onPress === 'function') {
|
||||
onPress();
|
||||
}
|
||||
}, [disabled, onPress]);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={({ pressed }) => [
|
||||
styles.menuItem,
|
||||
pressed && !disabled && styles.menuItemPressed,
|
||||
isDestructive && styles.menuItemDestructive,
|
||||
disabled && styles.menuItemDisabled,
|
||||
style
|
||||
]}
|
||||
onPress={handlePress}
|
||||
disabled={disabled}
|
||||
>
|
||||
<View style={styles.menuItemContent}>
|
||||
{icon ? <View style={styles.menuItemIcon}>{icon}</View> : null}
|
||||
<AppText
|
||||
style={[styles.menuItemText, isDestructive && styles.menuItemTextDestructive, disabled && styles.menuItemTextDisabled, textStyle]}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{title}
|
||||
</AppText>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = MenuItem;
|
||||
63
rn/app/src/components/menu/MenuList.js
Normal file
63
rn/app/src/components/menu/MenuList.js
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент списка элементов меню
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { ScrollView, View } = require('react-native'); //Базовые компоненты
|
||||
const MenuItem = require('./MenuItem'); //Элемент меню
|
||||
const EmptyMenu = require('./EmptyMenu'); //Пустое меню
|
||||
const MenuDivider = require('./MenuDivider'); //Разделитель меню
|
||||
const styles = require('../../styles/menu/MenuList.styles'); //Стили списка меню
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Список элементов меню
|
||||
function MenuList({ items = [], onClose, style }) {
|
||||
const handleItemPress = React.useCallback(
|
||||
item => {
|
||||
if (typeof onClose === 'function') {
|
||||
onClose();
|
||||
}
|
||||
if (typeof item.onPress === 'function') {
|
||||
item.onPress();
|
||||
}
|
||||
},
|
||||
[onClose]
|
||||
);
|
||||
|
||||
if (!Array.isArray(items) || items.length === 0) {
|
||||
return <EmptyMenu />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView style={[styles.scrollView, style]} showsVerticalScrollIndicator={false} bounces={false}>
|
||||
{items.map((item, index) => (
|
||||
<View key={item.id || `menu-item-${index}`}>
|
||||
<MenuItem
|
||||
title={item.title}
|
||||
icon={item.icon}
|
||||
onPress={() => handleItemPress(item)}
|
||||
isDestructive={item.isDestructive}
|
||||
disabled={item.disabled}
|
||||
style={item.style}
|
||||
textStyle={item.textStyle}
|
||||
/>
|
||||
{item.showDivider && index < items.length - 1 && <MenuDivider />}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = MenuList;
|
||||
162
rn/app/src/components/menu/SideMenu.js
Normal file
162
rn/app/src/components/menu/SideMenu.js
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Компонент бокового меню приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React
|
||||
const { Modal, View, Animated, Pressable } = require('react-native'); //Базовые компоненты
|
||||
const { useSafeAreaInsets } = require('react-native-safe-area-context'); //Отступы безопасной области
|
||||
const MenuHeader = require('./MenuHeader'); //Заголовок меню
|
||||
const MenuList = require('./MenuList'); //Список элементов меню
|
||||
const { widthPercentage, isTablet } = require('../../utils/responsive'); //Адаптивные утилиты
|
||||
const styles = require('../../styles/menu/SideMenu.styles'); //Стили бокового меню
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Ширина меню в зависимости от типа устройства
|
||||
const MENU_WIDTH_PHONE_PERCENT = 70;
|
||||
const MENU_WIDTH_TABLET_PERCENT = 40;
|
||||
const MENU_MAX_WIDTH = 360;
|
||||
const MENU_MIN_WIDTH = 280;
|
||||
|
||||
//Длительность анимации (мс)
|
||||
const ANIMATION_DURATION_OPEN = 250;
|
||||
const ANIMATION_DURATION_CLOSE = 200;
|
||||
|
||||
//Расчёт ширины меню с учётом адаптивности
|
||||
const calculateMenuWidth = () => {
|
||||
const percent = isTablet() ? MENU_WIDTH_TABLET_PERCENT : MENU_WIDTH_PHONE_PERCENT;
|
||||
const percentWidth = widthPercentage(percent);
|
||||
return Math.max(MENU_MIN_WIDTH, Math.min(percentWidth, MENU_MAX_WIDTH));
|
||||
};
|
||||
|
||||
//Боковое меню приложения
|
||||
function SideMenu({ visible, onClose, items = [], title = 'Меню', headerStyle, containerStyle, contentStyle }) {
|
||||
//Получаем отступы безопасной области
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
//Вычисляем ширину меню
|
||||
const menuWidth = calculateMenuWidth();
|
||||
|
||||
//Анимированные значения
|
||||
const translateX = React.useRef(new Animated.Value(menuWidth)).current;
|
||||
const backdropOpacity = React.useRef(new Animated.Value(0)).current;
|
||||
|
||||
//Флаг видимости модального окна
|
||||
const [modalVisible, setModalVisible] = React.useState(false);
|
||||
|
||||
//Стили с учётом safe area
|
||||
const safeAreaStyle = React.useMemo(
|
||||
() => ({
|
||||
paddingTop: insets.top,
|
||||
paddingBottom: insets.bottom,
|
||||
paddingRight: insets.right
|
||||
}),
|
||||
[insets.top, insets.bottom, insets.right]
|
||||
);
|
||||
|
||||
//Обработка открытия меню
|
||||
const openMenu = React.useCallback(() => {
|
||||
setModalVisible(true);
|
||||
translateX.setValue(menuWidth);
|
||||
backdropOpacity.setValue(0);
|
||||
|
||||
Animated.parallel([
|
||||
Animated.timing(translateX, {
|
||||
toValue: 0,
|
||||
duration: ANIMATION_DURATION_OPEN,
|
||||
useNativeDriver: true
|
||||
}),
|
||||
Animated.timing(backdropOpacity, {
|
||||
toValue: 1,
|
||||
duration: ANIMATION_DURATION_OPEN,
|
||||
useNativeDriver: true
|
||||
})
|
||||
]).start();
|
||||
}, [translateX, backdropOpacity, menuWidth]);
|
||||
|
||||
//Обработка закрытия меню
|
||||
const closeMenu = React.useCallback(() => {
|
||||
Animated.parallel([
|
||||
Animated.timing(translateX, {
|
||||
toValue: menuWidth,
|
||||
duration: ANIMATION_DURATION_CLOSE,
|
||||
useNativeDriver: true
|
||||
}),
|
||||
Animated.timing(backdropOpacity, {
|
||||
toValue: 0,
|
||||
duration: ANIMATION_DURATION_CLOSE,
|
||||
useNativeDriver: true
|
||||
})
|
||||
]).start(() => {
|
||||
setModalVisible(false);
|
||||
});
|
||||
}, [translateX, backdropOpacity, menuWidth]);
|
||||
|
||||
//Обработчик нажатия на задний фон
|
||||
const handleBackdropPress = React.useCallback(() => {
|
||||
if (typeof onClose === 'function') {
|
||||
onClose();
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
//Обработчик аппаратной кнопки "Назад" (Android)
|
||||
const handleRequestClose = React.useCallback(() => {
|
||||
if (typeof onClose === 'function') {
|
||||
onClose();
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
//Отслеживание изменения видимости
|
||||
React.useEffect(() => {
|
||||
if (visible) {
|
||||
openMenu();
|
||||
} else if (modalVisible) {
|
||||
closeMenu();
|
||||
}
|
||||
}, [visible, modalVisible, openMenu, closeMenu]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={modalVisible}
|
||||
transparent={true}
|
||||
animationType="none"
|
||||
statusBarTranslucent={true}
|
||||
onRequestClose={handleRequestClose}
|
||||
>
|
||||
<View style={styles.modalContainer}>
|
||||
<Animated.View style={[styles.backdrop, { opacity: backdropOpacity }]}>
|
||||
<Pressable style={styles.backdropPressable} onPress={handleBackdropPress} />
|
||||
</Animated.View>
|
||||
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.menuContainer,
|
||||
safeAreaStyle,
|
||||
{
|
||||
width: menuWidth,
|
||||
transform: [{ translateX }]
|
||||
},
|
||||
containerStyle
|
||||
]}
|
||||
>
|
||||
<MenuHeader title={title} onClose={onClose} style={[styles.header, headerStyle]} />
|
||||
|
||||
<MenuList items={items} onClose={onClose} style={contentStyle} />
|
||||
</Animated.View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SideMenu;
|
||||
96
rn/app/src/config/appConfig.js
Normal file
96
rn/app/src/config/appConfig.js
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Конфигурация приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const { responsiveSize, isTablet } = require('../utils/responsive'); //Адаптивные утилиты
|
||||
const { Platform } = require('react-native');
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Настройки сервера приложений
|
||||
const SYSTEM = {
|
||||
//Адрес сервера приложений
|
||||
SERVER: '',
|
||||
|
||||
//Таймаут сетевых запросов (мс)
|
||||
REQUEST_TIMEOUT: 30000,
|
||||
|
||||
//Минимальная версия Android для работы с SQLite
|
||||
MIN_ANDROID_VERSION: 7.0,
|
||||
|
||||
//Минимальная версия iOS для работы с SQLite
|
||||
MIN_IOS_VERSION: 11.0
|
||||
};
|
||||
|
||||
//Настройки локального хранилища
|
||||
const LOCAL_DB = {
|
||||
//Ключ для хранения данных предрейсовых осмотров
|
||||
INSPECTIONS_KEY: 'pretrip_inspections',
|
||||
|
||||
//Резервное хранилище для старых устройств (AsyncStorage)
|
||||
FALLBACK_STORAGE_KEY: 'pretrip_fallback_storage'
|
||||
};
|
||||
|
||||
//Настройки интерфейса
|
||||
const UI = {
|
||||
//Отступы по умолчанию (адаптивные)
|
||||
PADDING: responsiveSize(isTablet() ? 24 : 16),
|
||||
|
||||
//Радиус скругления по умолчанию (адаптивный)
|
||||
BORDER_RADIUS: responsiveSize(isTablet() ? 12 : 8),
|
||||
|
||||
//Размеры шрифтов
|
||||
FONT_SIZE_XS: responsiveSize(12),
|
||||
FONT_SIZE_SM: responsiveSize(14),
|
||||
FONT_SIZE_MD: responsiveSize(16),
|
||||
FONT_SIZE_LG: responsiveSize(18),
|
||||
FONT_SIZE_XL: responsiveSize(20),
|
||||
FONT_SIZE_2XL: responsiveSize(24),
|
||||
|
||||
//Высоты элементов
|
||||
BUTTON_HEIGHT: responsiveSize(Platform.OS === 'ios' ? 48 : 44),
|
||||
INPUT_HEIGHT: responsiveSize(Platform.OS === 'ios' ? 48 : 44),
|
||||
HEADER_HEIGHT: responsiveSize(isTablet() ? 80 : Platform.OS === 'ios' ? 70 : 56)
|
||||
};
|
||||
|
||||
//Проверка совместимости устройства
|
||||
const COMPATIBILITY = {
|
||||
//Проверяем версию Android
|
||||
isAndroidCompatible: () => {
|
||||
if (Platform.OS !== 'android') return true;
|
||||
|
||||
const majorVersion = parseInt(Platform.Version, 10);
|
||||
return majorVersion >= 24; // Android 7.0 = API 24
|
||||
},
|
||||
|
||||
//Проверяем версию iOS
|
||||
isIOSCompatible: () => {
|
||||
if (Platform.OS !== 'ios') return true;
|
||||
|
||||
const majorVersion = parseInt(Platform.Version, 10);
|
||||
return majorVersion >= 11; // iOS 11.0
|
||||
},
|
||||
|
||||
//Общая проверка совместимости
|
||||
isDeviceCompatible: () => {
|
||||
return COMPATIBILITY.isAndroidCompatible() && COMPATIBILITY.isIOSCompatible();
|
||||
}
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
SYSTEM,
|
||||
LOCAL_DB,
|
||||
UI,
|
||||
COMPATIBILITY
|
||||
};
|
||||
59
rn/app/src/config/theme.js
Normal file
59
rn/app/src/config/theme.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Единая цветовая схема (тема приложения)
|
||||
*/
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Базовая палитра приложения:
|
||||
//- основной фон и элементы интерфейса - белый;
|
||||
//- акцентный цвет - голубой.
|
||||
const APP_COLORS = {
|
||||
//Базовые
|
||||
white: '#FFFFFF',
|
||||
black: '#000000',
|
||||
|
||||
//Основной голубой акцент
|
||||
primary: '#2563EB',
|
||||
primaryDark: '#1D4ED8',
|
||||
primaryLight: '#60A5FA',
|
||||
primaryExtraLight: '#DBEAFE',
|
||||
|
||||
//Фоны
|
||||
background: '#F8FAFC',
|
||||
surface: '#FFFFFF',
|
||||
surfaceAlt: '#F1F5F9',
|
||||
|
||||
//Текст
|
||||
textPrimary: '#1E293B',
|
||||
textSecondary: '#64748B',
|
||||
textTertiary: '#94A3B8',
|
||||
textInverse: '#FFFFFF',
|
||||
|
||||
//Состояния
|
||||
error: '#DC2626',
|
||||
errorLight: '#FEE2E2',
|
||||
warning: '#F59E0B',
|
||||
warningLight: '#FEF3C7',
|
||||
success: '#16A34A',
|
||||
successLight: '#DCFCE7',
|
||||
|
||||
//Дополнительные
|
||||
borderSubtle: '#E2E8F0',
|
||||
borderMedium: '#CBD5E1',
|
||||
overlay: 'rgba(15, 23, 42, 0.5)',
|
||||
|
||||
//Семантические
|
||||
info: '#3B82F6',
|
||||
infoLight: '#DBEAFE'
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
APP_COLORS
|
||||
};
|
||||
351
rn/app/src/database/SQLiteDatabase.js
Normal file
351
rn/app/src/database/SQLiteDatabase.js
Normal file
@ -0,0 +1,351 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Модуль для работы с SQLite базой данных
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const { open } = require('react-native-quick-sqlite');
|
||||
|
||||
//Импорт утилиты для загрузки SQL файлов
|
||||
const SQLFileLoader = require('./sql/SQLFileLoader');
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
const DB_NAME = 'pretrip_inspections.db';
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
class SQLiteDatabase {
|
||||
constructor() {
|
||||
this.db = null;
|
||||
this.isInitialized = false;
|
||||
this.sqlQueries = null;
|
||||
}
|
||||
|
||||
//Инициализация базы данных
|
||||
async initialize() {
|
||||
if (this.isInitialized) {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
try {
|
||||
//Загружаем SQL файлы (синхронно)
|
||||
this.sqlQueries = SQLFileLoader.loadAllSQLFiles();
|
||||
|
||||
//Открываем базу данных
|
||||
this.db = open({
|
||||
name: DB_NAME,
|
||||
location: 'default'
|
||||
});
|
||||
|
||||
console.log('База данных успешно открыта');
|
||||
|
||||
//Настраиваем базу данных
|
||||
await this.setupDatabase();
|
||||
|
||||
this.isInitialized = true;
|
||||
return this.db;
|
||||
} catch (error) {
|
||||
console.error('Ошибка инициализации базы данных:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Настройка базы данных (создание таблиц и индексов)
|
||||
async setupDatabase() {
|
||||
if (!this.db || !this.sqlQueries) {
|
||||
throw new Error('База данных или SQL запросы не инициализированы');
|
||||
}
|
||||
|
||||
try {
|
||||
//Выполняем SQL запросы последовательно
|
||||
await this.executeQuery(this.sqlQueries.CREATE_TABLE_APP_SETTINGS);
|
||||
console.log('Таблица app_settings создана/проверена');
|
||||
|
||||
await this.executeQuery(this.sqlQueries.CREATE_TABLE_INSPECTIONS);
|
||||
console.log('Таблица inspections создана/проверена');
|
||||
|
||||
await this.executeQuery(this.sqlQueries.CREATE_INDEX_INSPECTIONS_STATUS);
|
||||
console.log('Индекс idx_inspections_status создан/проверен');
|
||||
|
||||
await this.executeQuery(this.sqlQueries.CREATE_INDEX_INSPECTIONS_CREATED);
|
||||
console.log('Индекс idx_inspections_created создан/проверен');
|
||||
|
||||
console.log('Все таблицы и индексы созданы/проверены');
|
||||
} catch (error) {
|
||||
console.error('Ошибка настройки базы данных:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Выполнение SQL запроса
|
||||
async executeQuery(sql, params = []) {
|
||||
if (!this.db) {
|
||||
throw new Error('База данных не инициализирована');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.db.executeAsync(sql, params);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Ошибка выполнения SQL запроса:', error, 'SQL:', sql, 'Params:', params);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Получение настройки
|
||||
async getSetting(key) {
|
||||
try {
|
||||
const result = await this.executeQuery(this.sqlQueries.SETTINGS_GET, [key]);
|
||||
|
||||
if (result.rows && result.rows.length > 0) {
|
||||
return result.rows.item(0).value;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения настройки:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Сохранение настройки
|
||||
async setSetting(key, value) {
|
||||
try {
|
||||
const stringValue = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
||||
await this.executeQuery(this.sqlQueries.SETTINGS_SET, [key, stringValue]);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка сохранения настройки:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Удаление настройки
|
||||
async deleteSetting(key) {
|
||||
try {
|
||||
await this.executeQuery(this.sqlQueries.SETTINGS_DELETE, [key]);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка удаления настройки:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Получение всех настроек
|
||||
async getAllSettings() {
|
||||
try {
|
||||
const result = await this.executeQuery(this.sqlQueries.SETTINGS_GET_ALL, []);
|
||||
const settings = {};
|
||||
|
||||
if (result.rows && result.rows.length > 0) {
|
||||
for (let i = 0; i < result.rows.length; i++) {
|
||||
const row = result.rows.item(i);
|
||||
try {
|
||||
settings[row.key] = JSON.parse(row.value);
|
||||
} catch {
|
||||
settings[row.key] = row.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения всех настроек:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Очистка всех настроек
|
||||
async clearSettings() {
|
||||
try {
|
||||
await this.executeQuery(this.sqlQueries.SETTINGS_CLEAR_ALL, []);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка очистки настроек:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Сохранение осмотра
|
||||
async saveInspection(inspection) {
|
||||
try {
|
||||
const { id, title, status, data } = inspection;
|
||||
const dataString = data ? JSON.stringify(data) : null;
|
||||
|
||||
await this.executeQuery(this.sqlQueries.INSPECTIONS_UPSERT, [id, title, status, id, dataString]);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка сохранения осмотра:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Получение всех осмотров
|
||||
async getInspections() {
|
||||
try {
|
||||
const result = await this.executeQuery(this.sqlQueries.INSPECTIONS_GET_ALL, []);
|
||||
const inspections = [];
|
||||
|
||||
if (result.rows && result.rows.length > 0) {
|
||||
for (let i = 0; i < result.rows.length; i++) {
|
||||
const row = result.rows.item(i);
|
||||
const inspection = {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
status: row.status,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at
|
||||
};
|
||||
|
||||
if (row.data) {
|
||||
try {
|
||||
inspection.data = JSON.parse(row.data);
|
||||
} catch {
|
||||
inspection.data = row.data;
|
||||
}
|
||||
}
|
||||
|
||||
inspections.push(inspection);
|
||||
}
|
||||
}
|
||||
|
||||
return inspections;
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения осмотров:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Получение осмотра по ID
|
||||
async getInspectionById(id) {
|
||||
try {
|
||||
const result = await this.executeQuery(this.sqlQueries.INSPECTIONS_GET_BY_ID, [id]);
|
||||
|
||||
if (result.rows && result.rows.length > 0) {
|
||||
const row = result.rows.item(0);
|
||||
const inspection = {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
status: row.status,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at
|
||||
};
|
||||
|
||||
if (row.data) {
|
||||
try {
|
||||
inspection.data = JSON.parse(row.data);
|
||||
} catch {
|
||||
inspection.data = row.data;
|
||||
}
|
||||
}
|
||||
|
||||
return inspection;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения осмотра по ID:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Удаление осмотра
|
||||
async deleteInspection(id) {
|
||||
try {
|
||||
await this.executeQuery(this.sqlQueries.INSPECTIONS_DELETE, [id]);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка удаления осмотра:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Удаление всех осмотров
|
||||
async clearInspections() {
|
||||
try {
|
||||
await this.executeQuery(this.sqlQueries.INSPECTIONS_DELETE_ALL, []);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка очистки осмотров:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Получение количества осмотров
|
||||
async getInspectionsCount() {
|
||||
try {
|
||||
const result = await this.executeQuery(this.sqlQueries.INSPECTIONS_COUNT, []);
|
||||
|
||||
if (result.rows && result.rows.length > 0) {
|
||||
return result.rows.item(0).count;
|
||||
}
|
||||
return 0;
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения количества осмотров:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Оптимизация базы данных
|
||||
async vacuum() {
|
||||
try {
|
||||
await this.executeQuery(this.sqlQueries.UTILITY_VACUUM, []);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка оптимизации базы данных:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Проверка существования таблицы
|
||||
async checkTableExists(tableName) {
|
||||
try {
|
||||
const result = await this.executeQuery(this.sqlQueries.UTILITY_CHECK_TABLE, [tableName]);
|
||||
return result.rows && result.rows.length > 0 && result.rows.item(0).exists === 1;
|
||||
} catch (error) {
|
||||
console.error('Ошибка проверки существования таблицы:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Удаление таблицы
|
||||
async dropTable(tableName) {
|
||||
try {
|
||||
await this.executeQuery(this.sqlQueries.UTILITY_DROP_TABLE, [tableName]);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Ошибка удаления таблицы:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Закрытие базы данных
|
||||
async close() {
|
||||
try {
|
||||
if (this.db) {
|
||||
// В react-native-quick-sqlite нет явного метода close
|
||||
// База данных закрывается автоматически при уничтожении объекта
|
||||
this.db = null;
|
||||
this.isInitialized = false;
|
||||
console.log('База данных закрыта');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка закрытия базы данных:', error);
|
||||
this.db = null;
|
||||
this.isInitialized = false;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------
|
||||
//Интерфейс модуля
|
||||
//-----------------
|
||||
|
||||
module.exports = new SQLiteDatabase();
|
||||
27
rn/app/src/database/sql/SQLFileLoader.js
Normal file
27
rn/app/src/database/sql/SQLFileLoader.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Утилита для загрузки SQL файлов
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const SQLQueries = require('./SQLQueries');
|
||||
|
||||
//------------
|
||||
//Тело модуля
|
||||
//------------
|
||||
|
||||
class SQLFileLoader {
|
||||
//Загрузка всех SQL файлов (синхронно)
|
||||
static loadAllSQLFiles() {
|
||||
return SQLQueries;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------
|
||||
//Интерфейс модуля
|
||||
//-----------------
|
||||
|
||||
module.exports = SQLFileLoader;
|
||||
70
rn/app/src/database/sql/SQLQueries.js
Normal file
70
rn/app/src/database/sql/SQLQueries.js
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Индексный файл для всех SQL запросов
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
//Таблицы
|
||||
const CREATE_TABLE_APP_SETTINGS = require('./settings/create_table_app_settings.sql');
|
||||
const CREATE_TABLE_INSPECTIONS = require('./inspections/create_table_inspections.sql');
|
||||
|
||||
//Индексы
|
||||
const CREATE_INDEX_INSPECTIONS_STATUS = require('./inspections/create_index_inspections_status.sql');
|
||||
const CREATE_INDEX_INSPECTIONS_CREATED = require('./inspections/create_index_inspections_created.sql');
|
||||
|
||||
//Настройки
|
||||
const SETTINGS_GET = require('./settings/get_setting.sql');
|
||||
const SETTINGS_SET = require('./settings/set_setting.sql');
|
||||
const SETTINGS_DELETE = require('./settings/delete_setting.sql');
|
||||
const SETTINGS_CLEAR_ALL = require('./settings/clear_all_settings.sql');
|
||||
const SETTINGS_GET_ALL = require('./settings/get_all_settings.sql');
|
||||
|
||||
//Осмотры
|
||||
const INSPECTIONS_INSERT = require('./inspections/insert_inspection.sql');
|
||||
const INSPECTIONS_UPSERT = require('./inspections/upsert_inspection.sql');
|
||||
const INSPECTIONS_GET_ALL = require('./inspections/get_all_inspections.sql');
|
||||
const INSPECTIONS_GET_BY_ID = require('./inspections/get_inspection_by_id.sql');
|
||||
const INSPECTIONS_DELETE = require('./inspections/delete_inspection.sql');
|
||||
const INSPECTIONS_DELETE_ALL = require('./inspections/delete_all_inspections.sql');
|
||||
const INSPECTIONS_COUNT = require('./inspections/count_inspections.sql');
|
||||
|
||||
//Утилиты
|
||||
const UTILITY_CHECK_TABLE = require('./utility/check_table_exists.sql');
|
||||
const UTILITY_DROP_TABLE = require('./utility/drop_table.sql');
|
||||
const UTILITY_VACUUM = require('./utility/vacuum.sql');
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Сбор всех SQL запросов в один объект
|
||||
const SQLQueries = {
|
||||
CREATE_TABLE_APP_SETTINGS,
|
||||
CREATE_TABLE_INSPECTIONS,
|
||||
CREATE_INDEX_INSPECTIONS_STATUS,
|
||||
CREATE_INDEX_INSPECTIONS_CREATED,
|
||||
SETTINGS_GET,
|
||||
SETTINGS_SET,
|
||||
SETTINGS_DELETE,
|
||||
SETTINGS_CLEAR_ALL,
|
||||
SETTINGS_GET_ALL,
|
||||
INSPECTIONS_INSERT,
|
||||
INSPECTIONS_UPSERT,
|
||||
INSPECTIONS_GET_ALL,
|
||||
INSPECTIONS_GET_BY_ID,
|
||||
INSPECTIONS_DELETE,
|
||||
INSPECTIONS_DELETE_ALL,
|
||||
INSPECTIONS_COUNT,
|
||||
UTILITY_CHECK_TABLE,
|
||||
UTILITY_DROP_TABLE,
|
||||
UTILITY_VACUUM
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SQLQueries;
|
||||
19
rn/app/src/database/sql/inspections/count_inspections.sql.js
Normal file
19
rn/app/src/database/sql/inspections/count_inspections.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: подсчет количества осмотров
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const INSPECTIONS_COUNT = `
|
||||
-- Подсчет количества осмотров
|
||||
SELECT COUNT(*) as count FROM inspections;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = INSPECTIONS_COUNT;
|
||||
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: создание индекса по дате создания
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const CREATE_INDEX_INSPECTIONS_CREATED = `
|
||||
-- Индекс для сортировки по дате создания
|
||||
CREATE INDEX IF NOT EXISTS idx_inspections_created ON inspections(created_at DESC);
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = CREATE_INDEX_INSPECTIONS_CREATED;
|
||||
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: создание индекса по статусу
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const CREATE_INDEX_INSPECTIONS_STATUS = `
|
||||
-- Индекс для быстрого поиска осмотров по статусу
|
||||
CREATE INDEX IF NOT EXISTS idx_inspections_status ON inspections(status);
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = CREATE_INDEX_INSPECTIONS_STATUS;
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: создание таблицы осмотров
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const CREATE_TABLE_INSPECTIONS = `
|
||||
-- Таблица для предрейсовых осмотров
|
||||
CREATE TABLE IF NOT EXISTS inspections (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
data TEXT
|
||||
);
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = CREATE_TABLE_INSPECTIONS;
|
||||
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: удаление всех осмотров
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const INSPECTIONS_DELETE_ALL = `
|
||||
-- Удаление всех осмотров
|
||||
DELETE FROM inspections;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = INSPECTIONS_DELETE_ALL;
|
||||
19
rn/app/src/database/sql/inspections/delete_inspection.sql.js
Normal file
19
rn/app/src/database/sql/inspections/delete_inspection.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: удаление осмотра
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const INSPECTIONS_DELETE = `
|
||||
-- Удаление осмотра по ID
|
||||
DELETE FROM inspections WHERE id = ?;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = INSPECTIONS_DELETE;
|
||||
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: получение всех осмотров
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const INSPECTIONS_GET_ALL = `
|
||||
-- Получение всех осмотров
|
||||
SELECT * FROM inspections ORDER BY created_at DESC;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = INSPECTIONS_GET_ALL;
|
||||
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: получение осмотра по ID
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const INSPECTIONS_GET_BY_ID = `
|
||||
-- Получение осмотра по ID
|
||||
SELECT * FROM inspections WHERE id = ?;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = INSPECTIONS_GET_BY_ID;
|
||||
20
rn/app/src/database/sql/inspections/insert_inspection.sql.js
Normal file
20
rn/app/src/database/sql/inspections/insert_inspection.sql.js
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: вставка нового осмотра
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const INSPECTIONS_INSERT = `
|
||||
-- Вставка нового осмотра
|
||||
INSERT INTO inspections (id, title, status, created_at, data)
|
||||
VALUES (?, ?, ?, ?, ?);
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = INSPECTIONS_INSERT;
|
||||
20
rn/app/src/database/sql/inspections/upsert_inspection.sql.js
Normal file
20
rn/app/src/database/sql/inspections/upsert_inspection.sql.js
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: вставка или обновление осмотра
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const INSPECTIONS_UPSERT = `
|
||||
-- Вставка или обновление осмотра
|
||||
INSERT OR REPLACE INTO inspections (id, title, status, created_at, updated_at, data)
|
||||
VALUES (?, ?, ?, COALESCE((SELECT created_at FROM inspections WHERE id = ?), CURRENT_TIMESTAMP), CURRENT_TIMESTAMP, ?);
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = INSPECTIONS_UPSERT;
|
||||
19
rn/app/src/database/sql/settings/clear_all_settings.sql.js
Normal file
19
rn/app/src/database/sql/settings/clear_all_settings.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: удаление всех настроек
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const SETTINGS_CLEAR_ALL = `
|
||||
-- Удаление всех настроек
|
||||
DELETE FROM app_settings;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SETTINGS_CLEAR_ALL;
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: создание таблицы настроек приложения
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const CREATE_TABLE_APP_SETTINGS = `
|
||||
-- Таблица для хранения настроек приложения
|
||||
CREATE TABLE IF NOT EXISTS app_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = CREATE_TABLE_APP_SETTINGS;
|
||||
19
rn/app/src/database/sql/settings/delete_setting.sql.js
Normal file
19
rn/app/src/database/sql/settings/delete_setting.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: удаление настройки
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const SETTINGS_DELETE = `
|
||||
-- Удаление настройки по ключу
|
||||
DELETE FROM app_settings WHERE key = ?;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SETTINGS_DELETE;
|
||||
19
rn/app/src/database/sql/settings/get_all_settings.sql.js
Normal file
19
rn/app/src/database/sql/settings/get_all_settings.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: получение всех настроек
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const SETTINGS_GET_ALL = `
|
||||
-- Получение всех настроек
|
||||
SELECT key, value FROM app_settings;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SETTINGS_GET_ALL;
|
||||
19
rn/app/src/database/sql/settings/get_setting.sql.js
Normal file
19
rn/app/src/database/sql/settings/get_setting.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: получение настройки по ключу
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const SETTINGS_GET = `
|
||||
-- Получение значения настройки по ключу
|
||||
SELECT value FROM app_settings WHERE key = ?;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SETTINGS_GET;
|
||||
20
rn/app/src/database/sql/settings/set_setting.sql.js
Normal file
20
rn/app/src/database/sql/settings/set_setting.sql.js
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: сохранение или обновление настройки
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const SETTINGS_SET = `
|
||||
-- Сохранение или обновление настройки
|
||||
INSERT OR REPLACE INTO app_settings (key, value, updated_at)
|
||||
VALUES (?, ?, CURRENT_TIMESTAMP);
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SETTINGS_SET;
|
||||
19
rn/app/src/database/sql/utility/check_table_exists.sql.js
Normal file
19
rn/app/src/database/sql/utility/check_table_exists.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: проверка существования таблицы
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const UTILITY_CHECK_TABLE = `
|
||||
-- Проверка существования таблицы
|
||||
SELECT 1 as exists FROM sqlite_master WHERE type='table' AND name=?;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = UTILITY_CHECK_TABLE;
|
||||
19
rn/app/src/database/sql/utility/drop_table.sql.js
Normal file
19
rn/app/src/database/sql/utility/drop_table.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: удаление таблицы
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const UTILITY_DROP_TABLE = `
|
||||
-- Удаление таблицы
|
||||
DROP TABLE IF EXISTS ?;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = UTILITY_DROP_TABLE;
|
||||
19
rn/app/src/database/sql/utility/vacuum.sql.js
Normal file
19
rn/app/src/database/sql/utility/vacuum.sql.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
SQL запрос: оптимизация базы данных
|
||||
*/
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
const UTILITY_VACUUM = `
|
||||
-- Оптимизация базы данных
|
||||
VACUUM;
|
||||
`;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = UTILITY_VACUUM;
|
||||
153
rn/app/src/hooks/useAppMessaging.js
Normal file
153
rn/app/src/hooks/useAppMessaging.js
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Хук сообщений приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const { APP_MESSAGE_VARIANT } = require('../components/common/AppMessage'); //Константы сообщений
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Начальное состояние сообщений
|
||||
const INITIAL_STATE = {
|
||||
visible: false,
|
||||
variant: APP_MESSAGE_VARIANT.INFO,
|
||||
title: '',
|
||||
message: '',
|
||||
buttons: null,
|
||||
containerStyle: null,
|
||||
contentStyle: null,
|
||||
titleStyle: null,
|
||||
messageStyle: null,
|
||||
headerStyle: null
|
||||
};
|
||||
|
||||
//Типы сообщений
|
||||
const MSG_TYPE = {
|
||||
INFO: APP_MESSAGE_VARIANT.INFO,
|
||||
WARN: APP_MESSAGE_VARIANT.WARN,
|
||||
ERR: APP_MESSAGE_VARIANT.ERR,
|
||||
SUCCESS: APP_MESSAGE_VARIANT.SUCCESS
|
||||
};
|
||||
|
||||
//Типы действий
|
||||
const MSG_AT = {
|
||||
SHOW_MSG: 'SHOW_MSG',
|
||||
HIDE_MSG: 'HIDE_MSG'
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Редьюсер состояния сообщений
|
||||
const messagingReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case MSG_AT.SHOW_MSG:
|
||||
return {
|
||||
...state,
|
||||
visible: true,
|
||||
variant: action.payload.variant || MSG_TYPE.INFO,
|
||||
title: action.payload.title || '',
|
||||
message: action.payload.message || '',
|
||||
buttons: action.payload.buttons || null,
|
||||
containerStyle: action.payload.containerStyle || null,
|
||||
contentStyle: action.payload.contentStyle || null,
|
||||
titleStyle: action.payload.titleStyle || null,
|
||||
messageStyle: action.payload.messageStyle || null,
|
||||
headerStyle: action.payload.headerStyle || null
|
||||
};
|
||||
case MSG_AT.HIDE_MSG:
|
||||
return {
|
||||
...state,
|
||||
visible: false
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
//Хук сообщений приложения
|
||||
function useAppMessaging() {
|
||||
const [state, dispatch] = React.useReducer(messagingReducer, INITIAL_STATE);
|
||||
|
||||
//Базовое отображение сообщения
|
||||
const showMessage = React.useCallback((payload = {}) => {
|
||||
dispatch({
|
||||
type: MSG_AT.SHOW_MSG,
|
||||
payload
|
||||
});
|
||||
}, []);
|
||||
|
||||
//Сообщение - ошибка
|
||||
const showError = React.useCallback(
|
||||
(message, options = {}) =>
|
||||
showMessage({
|
||||
...options,
|
||||
variant: MSG_TYPE.ERR,
|
||||
message
|
||||
}),
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
//Сообщение - информация
|
||||
const showInfo = React.useCallback(
|
||||
(message, options = {}) =>
|
||||
showMessage({
|
||||
...options,
|
||||
variant: MSG_TYPE.INFO,
|
||||
message
|
||||
}),
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
//Сообщение - предупреждение
|
||||
const showWarn = React.useCallback(
|
||||
(message, options = {}) =>
|
||||
showMessage({
|
||||
...options,
|
||||
variant: MSG_TYPE.WARN,
|
||||
message
|
||||
}),
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
//Сообщение - успех
|
||||
const showSuccess = React.useCallback(
|
||||
(message, options = {}) =>
|
||||
showMessage({
|
||||
...options,
|
||||
variant: MSG_TYPE.SUCCESS,
|
||||
message
|
||||
}),
|
||||
[showMessage]
|
||||
);
|
||||
|
||||
//Сокрытие сообщения
|
||||
const hideMessage = React.useCallback(() => {
|
||||
dispatch({ type: MSG_AT.HIDE_MSG });
|
||||
}, []);
|
||||
|
||||
return {
|
||||
MSG_TYPE,
|
||||
state,
|
||||
showMessage,
|
||||
showError,
|
||||
showInfo,
|
||||
showWarn,
|
||||
showSuccess,
|
||||
hideMessage
|
||||
};
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = useAppMessaging;
|
||||
112
rn/app/src/hooks/useAppMode.js
Normal file
112
rn/app/src/hooks/useAppMode.js
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Хук режима работы приложения (ONLINE / OFFLINE / NOT_CONNECTED)
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react');
|
||||
const { useAppLocalDbContext } = require('../components/layout/AppLocalDbProvider'); //Контекст локальной БД
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
const APP_MODE = {
|
||||
ONLINE: 'ONLINE',
|
||||
OFFLINE: 'OFFLINE',
|
||||
NOT_CONNECTED: 'NOT_CONNECTED'
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'pretrip_app_mode';
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Хук режима работы приложения
|
||||
function useAppMode() {
|
||||
const { getSetting, setSetting, isDbReady } = useAppLocalDbContext();
|
||||
const [mode, setMode] = React.useState(APP_MODE.NOT_CONNECTED);
|
||||
const [isInitialized, setIsInitialized] = React.useState(false);
|
||||
|
||||
//Предотвращение повторной загрузки
|
||||
const loadingRef = React.useRef(false);
|
||||
|
||||
//Загрузка режима из базы данных при готовности БД
|
||||
React.useEffect(() => {
|
||||
//Выходим, если БД не готова или уже загружаем/загрузили
|
||||
if (!isDbReady || loadingRef.current || isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingRef.current = true;
|
||||
|
||||
const loadMode = async () => {
|
||||
try {
|
||||
const savedMode = await getSetting(STORAGE_KEY);
|
||||
if (savedMode && Object.values(APP_MODE).includes(savedMode)) {
|
||||
setMode(savedMode);
|
||||
} else {
|
||||
const serverUrl = await getSetting('app_server_url');
|
||||
if (serverUrl) {
|
||||
setMode(APP_MODE.ONLINE);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки режима:', error);
|
||||
} finally {
|
||||
setIsInitialized(true);
|
||||
loadingRef.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
loadMode();
|
||||
}, [isDbReady, isInitialized, getSetting]);
|
||||
|
||||
//Сохранение режима в базу данных при изменении
|
||||
React.useEffect(() => {
|
||||
if (!isInitialized || !isDbReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
const saveMode = async () => {
|
||||
try {
|
||||
await setSetting(STORAGE_KEY, mode);
|
||||
} catch (error) {
|
||||
console.error('Ошибка сохранения режима:', error);
|
||||
}
|
||||
};
|
||||
|
||||
saveMode();
|
||||
}, [mode, isInitialized, isDbReady, setSetting]);
|
||||
|
||||
//Установка режима ONLINE
|
||||
const setOnline = React.useCallback(() => setMode(APP_MODE.ONLINE), []);
|
||||
|
||||
//Установка режима OFFLINE
|
||||
const setOffline = React.useCallback(() => setMode(APP_MODE.OFFLINE), []);
|
||||
|
||||
//Установка режима NOT_CONNECTED
|
||||
const setNotConnected = React.useCallback(() => setMode(APP_MODE.NOT_CONNECTED), []);
|
||||
|
||||
return {
|
||||
APP_MODE,
|
||||
mode,
|
||||
setMode,
|
||||
setOnline,
|
||||
setOffline,
|
||||
setNotConnected,
|
||||
isOnline: mode === APP_MODE.ONLINE,
|
||||
isOffline: mode === APP_MODE.OFFLINE,
|
||||
isNotConnected: mode === APP_MODE.NOT_CONNECTED
|
||||
};
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = useAppMode;
|
||||
86
rn/app/src/hooks/useAppNavigation.js
Normal file
86
rn/app/src/hooks/useAppNavigation.js
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Хук навигации приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Экраны приложения
|
||||
const SCREENS = {
|
||||
MAIN: 'MAIN',
|
||||
SETTINGS: 'SETTINGS'
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Хук навигации приложения
|
||||
const useAppNavigation = () => {
|
||||
const [navigationState, setNavigationState] = React.useState({
|
||||
currentScreen: SCREENS.MAIN,
|
||||
screenParams: {},
|
||||
history: [SCREENS.MAIN]
|
||||
});
|
||||
|
||||
//Навигация на экран
|
||||
const navigate = React.useCallback((screen, params = {}) => {
|
||||
setNavigationState(prev => ({
|
||||
currentScreen: screen,
|
||||
screenParams: params,
|
||||
history: [...prev.history, screen]
|
||||
}));
|
||||
}, []);
|
||||
|
||||
//Возврат назад
|
||||
const goBack = React.useCallback(() => {
|
||||
setNavigationState(prev => {
|
||||
if (prev.history.length <= 1) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
const newHistory = [...prev.history];
|
||||
newHistory.pop();
|
||||
const previousScreen = newHistory[newHistory.length - 1];
|
||||
|
||||
return {
|
||||
currentScreen: previousScreen,
|
||||
screenParams: {},
|
||||
history: newHistory
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
//Сброс навигации
|
||||
const reset = React.useCallback(() => {
|
||||
setNavigationState({
|
||||
currentScreen: SCREENS.MAIN,
|
||||
screenParams: {},
|
||||
history: [SCREENS.MAIN]
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
SCREENS,
|
||||
currentScreen: navigationState.currentScreen,
|
||||
screenParams: navigationState.screenParams,
|
||||
navigate,
|
||||
goBack,
|
||||
reset,
|
||||
canGoBack: navigationState.history.length > 1
|
||||
};
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = useAppNavigation;
|
||||
146
rn/app/src/hooks/useAppServer.js
Normal file
146
rn/app/src/hooks/useAppServer.js
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Хук для взаимодействия с сервером приложений
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react');
|
||||
const { useAppLocalDbContext } = require('../components/layout/AppLocalDbProvider'); //Контекст локальной БД
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
const RESP_STATUS_OK = 'OK';
|
||||
const RESP_STATUS_ERR = 'ERR';
|
||||
|
||||
const ERR_APPSERVER = 'Ошибка сервера приложений';
|
||||
const ERR_NETWORK = 'Ошибка соединения с сервером';
|
||||
const ERR_UNEXPECTED = 'Неожиданный ответ сервера';
|
||||
const ERR_ABORTED = 'Запрос прерван принудительно';
|
||||
|
||||
//-----------------------
|
||||
//Вспомогательные функции
|
||||
//-----------------------
|
||||
|
||||
//Создание объекта ошибки ответа
|
||||
const makeRespErr = ({ message }) => ({
|
||||
status: RESP_STATUS_ERR,
|
||||
message
|
||||
});
|
||||
|
||||
//Проверка является ли ответ ошибкой
|
||||
const isRespErr = resp => resp && resp.status === RESP_STATUS_ERR;
|
||||
|
||||
//Получение сообщения об ошибке из ответа
|
||||
const getRespErrMessage = resp => (isRespErr(resp) && resp.message ? resp.message : '');
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Хук для взаимодействия с сервером приложений
|
||||
function useAppServer() {
|
||||
const { getSetting } = useAppLocalDbContext();
|
||||
const abortControllerRef = React.useRef(null);
|
||||
|
||||
//Принудительная отмена активного запроса
|
||||
const abort = React.useCallback(() => {
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort();
|
||||
}
|
||||
}, []);
|
||||
|
||||
//Выполнение произвольного действия на сервере
|
||||
const executeAction = React.useCallback(
|
||||
async ({ path = '', method = 'POST', payload = null } = {}) => {
|
||||
let baseURL = '';
|
||||
try {
|
||||
baseURL = (await getSetting('app_server_url')) || '';
|
||||
} catch (e) {
|
||||
console.warn('Не удалось прочитать настройки сервера:', e);
|
||||
}
|
||||
|
||||
if (!baseURL) {
|
||||
return makeRespErr({
|
||||
message: 'Адрес сервера не настроен. Пожалуйста, настройте сервер в настройках приложения.'
|
||||
});
|
||||
}
|
||||
|
||||
const normalizedBaseURL = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
|
||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
||||
const url = `${normalizedBaseURL}${normalizedPath}`;
|
||||
|
||||
const abortController = new AbortController();
|
||||
abortControllerRef.current = abortController;
|
||||
|
||||
let response = null;
|
||||
let responseJSON = null;
|
||||
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: payload ? JSON.stringify(payload) : null,
|
||||
signal: abortController.signal
|
||||
});
|
||||
} catch (e) {
|
||||
if (abortController.signal.aborted === true) {
|
||||
return makeRespErr({ message: ERR_ABORTED });
|
||||
}
|
||||
return makeRespErr({
|
||||
message: `${ERR_NETWORK}: ${e.message || 'неопределённая ошибка'}`
|
||||
});
|
||||
} finally {
|
||||
abortControllerRef.current = null;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
return makeRespErr({
|
||||
message: `${ERR_APPSERVER}: ${response.status} ${response.statusText || ''}`
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
responseJSON = await response.json();
|
||||
} catch (e) {
|
||||
return makeRespErr({ message: ERR_UNEXPECTED });
|
||||
}
|
||||
|
||||
if (!responseJSON || !responseJSON.status) {
|
||||
return makeRespErr({ message: ERR_UNEXPECTED });
|
||||
}
|
||||
|
||||
return responseJSON;
|
||||
},
|
||||
[getSetting]
|
||||
);
|
||||
|
||||
//Автоматическая отмена при размонтировании хука
|
||||
React.useEffect(
|
||||
() => () => {
|
||||
abort();
|
||||
},
|
||||
[abort]
|
||||
);
|
||||
|
||||
return {
|
||||
executeAction,
|
||||
abort,
|
||||
isRespErr,
|
||||
getRespErrMessage,
|
||||
RESP_STATUS_OK,
|
||||
RESP_STATUS_ERR
|
||||
};
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = useAppServer;
|
||||
48
rn/app/src/hooks/useHardwareBackPress.js
Normal file
48
rn/app/src/hooks/useHardwareBackPress.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Хук обработки аппаратной кнопки "Назад" (Android)
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const { BackHandler, Platform } = require('react-native'); //BackHandler и платформа
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Хук обработки аппаратной кнопки "Назад"
|
||||
const useHardwareBackPress = (handler, deps = []) => {
|
||||
React.useEffect(() => {
|
||||
//BackHandler работает только на Android
|
||||
if (Platform.OS !== 'android') {
|
||||
return;
|
||||
}
|
||||
|
||||
//Обработчик нажатия кнопки "Назад"
|
||||
const backHandler = () => {
|
||||
if (typeof handler === 'function') {
|
||||
return handler();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
//Подписка на событие
|
||||
const subscription = BackHandler.addEventListener('hardwareBackPress', backHandler);
|
||||
|
||||
//Отписка при размонтировании
|
||||
return () => {
|
||||
subscription.remove();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, deps);
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = useHardwareBackPress;
|
||||
274
rn/app/src/hooks/useLocalDb.js
Normal file
274
rn/app/src/hooks/useLocalDb.js
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Хук для работы с локальной базой данных SQLite
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react');
|
||||
const SQLiteDatabase = require('../database/SQLiteDatabase');
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
function useLocalDb() {
|
||||
//Состояние хука
|
||||
const [isDbReady, setIsDbReady] = React.useState(false);
|
||||
const [inspections, setInspections] = React.useState([]);
|
||||
const [dbError, setDbError] = React.useState(null);
|
||||
|
||||
//Отслеживания статуса инициализации
|
||||
const initializingRef = React.useRef(false);
|
||||
|
||||
//Инициализация базы данных при монтировании
|
||||
React.useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
//Функция инициализации БД
|
||||
const initDb = async () => {
|
||||
//Предотвращаем повторную инициализацию
|
||||
if (initializingRef.current || SQLiteDatabase.isInitialized) {
|
||||
if (mounted && SQLiteDatabase.isInitialized) {
|
||||
setIsDbReady(true);
|
||||
try {
|
||||
const loadedInspections = await SQLiteDatabase.getInspections();
|
||||
setInspections(loadedInspections);
|
||||
} catch (loadError) {
|
||||
console.error('Ошибка загрузки осмотров при переинициализации:', loadError);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
initializingRef.current = true;
|
||||
|
||||
try {
|
||||
await SQLiteDatabase.initialize();
|
||||
if (mounted) {
|
||||
setIsDbReady(true);
|
||||
const loadedInspections = await SQLiteDatabase.getInspections();
|
||||
setInspections(loadedInspections);
|
||||
setDbError(null);
|
||||
}
|
||||
} catch (initError) {
|
||||
console.error('Ошибка инициализации базы данных:', initError);
|
||||
if (mounted) {
|
||||
setIsDbReady(false);
|
||||
setDbError('Не удалось инициализировать базу данных');
|
||||
}
|
||||
} finally {
|
||||
initializingRef.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
initDb();
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
//Загрузка списка осмотров
|
||||
const loadInspections = React.useCallback(async () => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const loadedInspections = await SQLiteDatabase.getInspections();
|
||||
setInspections(loadedInspections);
|
||||
setDbError(null);
|
||||
return loadedInspections;
|
||||
} catch (loadError) {
|
||||
console.error('Ошибка загрузки осмотров:', loadError);
|
||||
setDbError('Не удалось загрузить осмотры');
|
||||
return [];
|
||||
}
|
||||
}, [isDbReady]);
|
||||
|
||||
//Сохранение осмотра
|
||||
const saveInspection = React.useCallback(
|
||||
async inspection => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return inspection;
|
||||
}
|
||||
|
||||
try {
|
||||
await SQLiteDatabase.saveInspection(inspection);
|
||||
const updatedInspections = await SQLiteDatabase.getInspections();
|
||||
setInspections(updatedInspections);
|
||||
setDbError(null);
|
||||
return inspection;
|
||||
} catch (saveError) {
|
||||
console.error('Ошибка сохранения осмотра:', saveError);
|
||||
setDbError('Не удалось сохранить осмотр');
|
||||
return inspection;
|
||||
}
|
||||
},
|
||||
[isDbReady]
|
||||
);
|
||||
|
||||
//Получение настройки
|
||||
const getSetting = React.useCallback(
|
||||
async key => {
|
||||
if (!isDbReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return await SQLiteDatabase.getSetting(key);
|
||||
} catch (getSettingError) {
|
||||
console.error('Ошибка получения настройки:', getSettingError);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[isDbReady]
|
||||
);
|
||||
|
||||
//Сохранение настройки
|
||||
const setSetting = React.useCallback(
|
||||
async (key, value) => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await SQLiteDatabase.setSetting(key, value);
|
||||
} catch (setSettingError) {
|
||||
console.error('Ошибка сохранения настройки:', setSettingError);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[isDbReady]
|
||||
);
|
||||
|
||||
//Удаление настройки
|
||||
const deleteSetting = React.useCallback(
|
||||
async key => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await SQLiteDatabase.deleteSetting(key);
|
||||
} catch (deleteSettingError) {
|
||||
console.error('Ошибка удаления настройки:', deleteSettingError);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[isDbReady]
|
||||
);
|
||||
|
||||
//Получение всех настроек
|
||||
const getAllSettings = React.useCallback(async () => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return await SQLiteDatabase.getAllSettings();
|
||||
} catch (getAllSettingsError) {
|
||||
console.error('Ошибка получения всех настроек:', getAllSettingsError);
|
||||
return {};
|
||||
}
|
||||
}, [isDbReady]);
|
||||
|
||||
//Очистка всех настроек
|
||||
const clearSettings = React.useCallback(async () => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await SQLiteDatabase.clearSettings();
|
||||
} catch (clearSettingsError) {
|
||||
console.error('Ошибка очистки настроек:', clearSettingsError);
|
||||
return false;
|
||||
}
|
||||
}, [isDbReady]);
|
||||
|
||||
//Очистка всех осмотров
|
||||
const clearInspections = React.useCallback(async () => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await SQLiteDatabase.clearInspections();
|
||||
if (result) {
|
||||
setInspections([]);
|
||||
setDbError(null);
|
||||
}
|
||||
return result;
|
||||
} catch (clearInspectionsError) {
|
||||
console.error('Ошибка очистки осмотров:', clearInspectionsError);
|
||||
setDbError('Не удалось очистить осмотры');
|
||||
return false;
|
||||
}
|
||||
}, [isDbReady]);
|
||||
|
||||
//Оптимизация базы данных
|
||||
const vacuum = React.useCallback(async () => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await SQLiteDatabase.vacuum();
|
||||
} catch (vacuumError) {
|
||||
console.error('Ошибка оптимизации базы данных:', vacuumError);
|
||||
return false;
|
||||
}
|
||||
}, [isDbReady]);
|
||||
|
||||
//Проверка существования таблицы
|
||||
const checkTableExists = React.useCallback(
|
||||
async tableName => {
|
||||
if (!isDbReady) {
|
||||
console.warn('База данных не готова');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await SQLiteDatabase.checkTableExists(tableName);
|
||||
} catch (checkTableError) {
|
||||
console.error('Ошибка проверки существования таблицы:', checkTableError);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[isDbReady]
|
||||
);
|
||||
|
||||
return {
|
||||
isDbReady,
|
||||
inspections,
|
||||
error: dbError,
|
||||
loadInspections,
|
||||
saveInspection,
|
||||
getSetting,
|
||||
setSetting,
|
||||
deleteSetting,
|
||||
getAllSettings,
|
||||
clearSettings,
|
||||
clearInspections,
|
||||
vacuum,
|
||||
checkTableExists
|
||||
};
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = useLocalDb;
|
||||
147
rn/app/src/hooks/usePreTripInspections.js
Normal file
147
rn/app/src/hooks/usePreTripInspections.js
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Хук предметной области "Предрейсовые осмотры"
|
||||
|
||||
Объединяет:
|
||||
- работу с сервером приложений;
|
||||
- работу с локальной базой данных;
|
||||
- управление состоянием загрузки/ошибок.
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const useAppServer = require('./useAppServer'); //Хук для сервера приложений
|
||||
const { useAppLocalDbContext } = require('../components/layout/AppLocalDbProvider'); //Контекст локальной БД
|
||||
const useAppMode = require('./useAppMode'); //Хук режима работы приложения
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Статусы загрузки данных
|
||||
const LOAD_STATUS_IDLE = 'IDLE';
|
||||
const LOAD_STATUS_LOADING = 'LOADING';
|
||||
const LOAD_STATUS_DONE = 'DONE';
|
||||
const LOAD_STATUS_ERROR = 'ERROR';
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Хук предметной области "Предрейсовые осмотры"
|
||||
function usePreTripInspections() {
|
||||
const { executeAction, isRespErr, getRespErrMessage, RESP_STATUS_OK } = useAppServer();
|
||||
const { inspections, loadInspections, saveInspection, isDbReady } = useAppLocalDbContext();
|
||||
const { APP_MODE, mode } = useAppMode();
|
||||
|
||||
const [loadStatus, setLoadStatus] = React.useState(LOAD_STATUS_IDLE);
|
||||
const [error, setError] = React.useState(null);
|
||||
|
||||
//Загрузка списка осмотров:
|
||||
//1) Если режим OFFLINE - работаем только с локальной БД;
|
||||
//2) Если режим ONLINE - пробуем получить данные с сервера приложений,
|
||||
// при ошибке используем локальные данные.
|
||||
const refreshInspections = React.useCallback(async () => {
|
||||
//Проверяем готовность базы данных
|
||||
if (!isDbReady) {
|
||||
return {
|
||||
inspections: [],
|
||||
fromServer: false
|
||||
};
|
||||
}
|
||||
|
||||
setLoadStatus(LOAD_STATUS_LOADING);
|
||||
setError(null);
|
||||
|
||||
//Режим OFFLINE - работаем только с локальными данными
|
||||
if (mode === APP_MODE.OFFLINE) {
|
||||
const localInspections = await loadInspections();
|
||||
setLoadStatus(LOAD_STATUS_DONE);
|
||||
return {
|
||||
inspections: localInspections,
|
||||
fromServer: false
|
||||
};
|
||||
}
|
||||
|
||||
//Режим ONLINE - пробуем получить данные с сервера приложений
|
||||
const serverResponse = await executeAction({
|
||||
path: 'api/pretrip/inspections',
|
||||
method: 'POST',
|
||||
payload: {}
|
||||
});
|
||||
|
||||
//Ошибка сервера приложений - пробуем взять данные из локальной БД
|
||||
if (isRespErr(serverResponse) || serverResponse.status !== RESP_STATUS_OK) {
|
||||
const localInspections = await loadInspections();
|
||||
setLoadStatus(localInspections.length > 0 ? LOAD_STATUS_DONE : LOAD_STATUS_ERROR);
|
||||
setError(getRespErrMessage(serverResponse) || 'Не удалось получить данные с сервера приложений');
|
||||
return {
|
||||
inspections: localInspections,
|
||||
fromServer: false
|
||||
};
|
||||
}
|
||||
|
||||
//Успех - приводим полезную нагрузку к ожидаемому виду
|
||||
const payload = serverResponse.payload || {};
|
||||
const resultInspections = Array.isArray(payload.inspections) ? payload.inspections : [];
|
||||
|
||||
setLoadStatus(LOAD_STATUS_DONE);
|
||||
|
||||
return {
|
||||
inspections: resultInspections,
|
||||
fromServer: true
|
||||
};
|
||||
}, [APP_MODE.OFFLINE, RESP_STATUS_OK, executeAction, getRespErrMessage, isDbReady, isRespErr, loadInspections, mode]);
|
||||
|
||||
//Создание или обновление осмотра
|
||||
const upsertInspection = React.useCallback(
|
||||
async inspection => {
|
||||
const safeInspection = {
|
||||
id: inspection.id || `local-${Date.now()}`,
|
||||
title: inspection.title || 'Новый осмотр',
|
||||
status: inspection.status || 'DRAFT',
|
||||
createdAt: inspection.createdAt || new Date().toISOString()
|
||||
};
|
||||
|
||||
//Сохраняем в локальной БД (всегда, независимо от режима)
|
||||
await saveInspection(safeInspection);
|
||||
|
||||
//Если приложение в режиме ONLINE - отправляем данные на сервер приложений
|
||||
//TODO: вызов конкретного метода сервера
|
||||
if (mode === APP_MODE.ONLINE) {
|
||||
await executeAction({
|
||||
path: 'api/pretrip/inspections/save',
|
||||
method: 'POST',
|
||||
payload: { inspection: safeInspection }
|
||||
});
|
||||
}
|
||||
|
||||
return safeInspection;
|
||||
},
|
||||
[APP_MODE.ONLINE, executeAction, saveInspection, mode]
|
||||
);
|
||||
|
||||
return {
|
||||
inspections,
|
||||
loadStatus,
|
||||
error,
|
||||
isDbReady,
|
||||
refreshInspections,
|
||||
upsertInspection
|
||||
};
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = {
|
||||
usePreTripInspections,
|
||||
LOAD_STATUS_IDLE,
|
||||
LOAD_STATUS_LOADING,
|
||||
LOAD_STATUS_DONE,
|
||||
LOAD_STATUS_ERROR
|
||||
};
|
||||
136
rn/app/src/screens/MainScreen.js
Normal file
136
rn/app/src/screens/MainScreen.js
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Главный экран приложения с боковым меню
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('react'); //React и хуки
|
||||
const { View } = require('react-native'); //Базовые компоненты
|
||||
const { LOAD_STATUS_LOADING } = require('../hooks/usePreTripInspections'); //Константы загрузки
|
||||
const { useAppMessagingContext } = require('../components/layout/AppMessagingProvider'); //Контекст сообщений
|
||||
const { useAppModeContext } = require('../components/layout/AppModeProvider'); //Контекст режима работы
|
||||
const { useAppNavigationContext } = require('../components/layout/AppNavigationProvider'); //Контекст навигации
|
||||
const { useAppLocalDbContext } = require('../components/layout/AppLocalDbProvider'); //Контекст локальной БД
|
||||
const { useAppPreTripInspectionsContext } = require('../components/layout/AppPreTripInspectionsProvider'); //Контекст осмотров
|
||||
const AppHeader = require('../components/layout/AppHeader'); //Заголовок с меню
|
||||
const SideMenu = require('../components/menu/SideMenu'); //Боковое меню
|
||||
const InspectionList = require('../components/inspections/InspectionList'); //Список осмотров
|
||||
const { getAppInfo } = require('../utils/appInfo'); //Информация о приложении
|
||||
const styles = require('../styles/screens/MainScreen.styles'); //Стили экрана
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Главный экран приложения
|
||||
function MainScreen() {
|
||||
const { inspections, loadStatus, error, isDbReady, refreshInspections } = useAppPreTripInspectionsContext();
|
||||
const { showInfo } = useAppMessagingContext();
|
||||
const { mode } = useAppModeContext();
|
||||
const { navigate, SCREENS } = useAppNavigationContext();
|
||||
const { getSetting, isDbReady: isLocalDbReady } = useAppLocalDbContext();
|
||||
|
||||
const [menuVisible, setMenuVisible] = React.useState(false);
|
||||
const [serverUrl, setServerUrl] = React.useState('');
|
||||
|
||||
//Предотвращение повторной загрузки при монтировании
|
||||
const initialLoadRef = React.useRef(false);
|
||||
|
||||
//Загрузка URL сервера при готовности БД
|
||||
React.useEffect(() => {
|
||||
if (!isLocalDbReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadServerUrl = async () => {
|
||||
try {
|
||||
const savedUrl = await getSetting('app_server_url');
|
||||
if (savedUrl) {
|
||||
setServerUrl(savedUrl);
|
||||
}
|
||||
} catch (loadError) {
|
||||
console.error('Ошибка загрузки URL сервера:', loadError);
|
||||
}
|
||||
};
|
||||
|
||||
loadServerUrl();
|
||||
}, [isLocalDbReady, getSetting]);
|
||||
|
||||
//Первичная загрузка данных
|
||||
React.useEffect(() => {
|
||||
//Выходим, если БД не готова или уже загружали
|
||||
if (!isDbReady || initialLoadRef.current) {
|
||||
return;
|
||||
}
|
||||
initialLoadRef.current = true;
|
||||
refreshInspections();
|
||||
}, [isDbReady, refreshInspections]);
|
||||
|
||||
//Обработчик открытия меню
|
||||
const handleMenuOpen = React.useCallback(() => {
|
||||
setMenuVisible(true);
|
||||
}, []);
|
||||
|
||||
//Обработчик закрытия меню
|
||||
const handleMenuClose = React.useCallback(() => {
|
||||
setMenuVisible(false);
|
||||
}, []);
|
||||
|
||||
//Обработчик показа информации о приложении
|
||||
const handleShowAbout = React.useCallback(() => {
|
||||
const appInfo = getAppInfo({
|
||||
mode,
|
||||
serverUrl,
|
||||
isDbReady: isLocalDbReady
|
||||
});
|
||||
|
||||
showInfo(appInfo, {
|
||||
title: 'Информация о приложении'
|
||||
});
|
||||
}, [showInfo, mode, serverUrl, isLocalDbReady]);
|
||||
|
||||
//Пункты бокового меню
|
||||
const menuItems = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'settings',
|
||||
title: 'Настройки',
|
||||
onPress: () => {
|
||||
navigate(SCREENS.SETTINGS);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
title: 'О приложении',
|
||||
onPress: handleShowAbout
|
||||
}
|
||||
],
|
||||
[navigate, handleShowAbout, SCREENS.SETTINGS]
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<AppHeader onMenuPress={handleMenuOpen} />
|
||||
|
||||
<View style={styles.content}>
|
||||
<InspectionList
|
||||
inspections={inspections}
|
||||
isLoading={loadStatus === LOAD_STATUS_LOADING}
|
||||
error={error}
|
||||
onRefresh={refreshInspections}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<SideMenu visible={menuVisible} onClose={handleMenuClose} items={menuItems} title="Меню" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = MainScreen;
|
||||
379
rn/app/src/screens/SettingsScreen.js
Normal file
379
rn/app/src/screens/SettingsScreen.js
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Экран настроек приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const React = require('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 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 { useAppLocalDbContext } = require('../components/layout/AppLocalDbProvider');
|
||||
const { APP_COLORS } = require('../config/theme');
|
||||
const { getAppInfo, getModeLabel } = require('../utils/appInfo');
|
||||
const styles = require('../styles/screens/SettingsScreen.styles');
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
function SettingsScreen() {
|
||||
const { showInfo, showError, showSuccess } = useAppMessagingContext();
|
||||
const { mode, setOnline, setNotConnected } = useAppModeContext();
|
||||
const { goBack, canGoBack } = useAppNavigationContext();
|
||||
const { getSetting, setSetting, clearSettings, clearInspections, vacuum, isDbReady } = useAppLocalDbContext();
|
||||
|
||||
const [serverUrl, setServerUrl] = React.useState('');
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [isServerUrlDialogVisible, setIsServerUrlDialogVisible] = React.useState(false);
|
||||
|
||||
//Предотвращение повторной загрузки настроек
|
||||
const settingsLoadedRef = React.useRef(false);
|
||||
|
||||
//Загрузка сохраненного URL сервера при готовности БД
|
||||
React.useEffect(() => {
|
||||
//Выходим, если БД не готова или уже загрузили настройки
|
||||
if (!isDbReady || settingsLoadedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
settingsLoadedRef.current = true;
|
||||
|
||||
const loadSettings = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const savedUrl = await getSetting('app_server_url');
|
||||
if (savedUrl) {
|
||||
setServerUrl(savedUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки настроек:', error);
|
||||
showError('Не удалось загрузить настройки');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadSettings();
|
||||
}, [isDbReady, getSetting, showError]);
|
||||
|
||||
//Открытие диалога ввода URL сервера
|
||||
const handleOpenServerUrlDialog = React.useCallback(() => {
|
||||
setIsServerUrlDialogVisible(true);
|
||||
}, []);
|
||||
|
||||
//Закрытие диалога ввода URL сервера
|
||||
const handleCloseServerUrlDialog = React.useCallback(() => {
|
||||
setIsServerUrlDialogVisible(false);
|
||||
}, []);
|
||||
|
||||
//Валидатор URL сервера
|
||||
const validateServerUrl = React.useCallback(url => {
|
||||
if (!url || !url.trim()) {
|
||||
return 'Введите адрес сервера';
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedUrl = new URL(url.trim());
|
||||
if (!parsedUrl.protocol.startsWith('http')) {
|
||||
return 'Используйте http:// или https:// протокол';
|
||||
}
|
||||
} catch (error) {
|
||||
return 'Некорректный формат URL';
|
||||
}
|
||||
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
//Сохранение настроек сервера
|
||||
const handleSaveServerUrl = React.useCallback(
|
||||
async url => {
|
||||
setIsServerUrlDialogVisible(false);
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const success = await setSetting('app_server_url', url);
|
||||
|
||||
if (success) {
|
||||
setServerUrl(url);
|
||||
showSuccess('Настройки сервера сохранены');
|
||||
|
||||
if (mode === 'NOT_CONNECTED') {
|
||||
setOnline();
|
||||
}
|
||||
} else {
|
||||
showError('Не удалось сохранить настройки');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка сохранения настроек:', error);
|
||||
showError('Не удалось сохранить настройки');
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
},
|
||||
[mode, setOnline, setSetting, showError, showSuccess]
|
||||
);
|
||||
|
||||
//Очистка кэша (осмотров)
|
||||
const handleClearCache = React.useCallback(async () => {
|
||||
showInfo('Очистить кэш приложения?', {
|
||||
title: 'Подтверждение',
|
||||
buttons: [
|
||||
{
|
||||
id: 'cancel',
|
||||
title: 'Отмена',
|
||||
onPress: () => { }
|
||||
},
|
||||
{
|
||||
id: 'confirm',
|
||||
title: 'Очистить',
|
||||
onPress: async () => {
|
||||
try {
|
||||
const success = await clearInspections();
|
||||
if (success) {
|
||||
showSuccess('Кэш успешно очищен');
|
||||
} else {
|
||||
showError('Не удалось очистить кэш');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка очистки кэша:', error);
|
||||
showError('Не удалось очистить кэш');
|
||||
}
|
||||
},
|
||||
buttonStyle: { backgroundColor: APP_COLORS.error },
|
||||
textStyle: { color: APP_COLORS.white }
|
||||
}
|
||||
]
|
||||
});
|
||||
}, [showInfo, showSuccess, showError, clearInspections]);
|
||||
|
||||
//Сброс всех настроек
|
||||
const handleResetSettings = React.useCallback(async () => {
|
||||
showInfo('Сбросить все настройки к значениям по умолчанию?', {
|
||||
title: 'Подтверждение сброса',
|
||||
buttons: [
|
||||
{
|
||||
id: 'cancel',
|
||||
title: 'Отмена',
|
||||
onPress: () => { }
|
||||
},
|
||||
{
|
||||
id: 'confirm',
|
||||
title: 'Сбросить',
|
||||
onPress: async () => {
|
||||
try {
|
||||
const success = await clearSettings();
|
||||
if (success) {
|
||||
setServerUrl('');
|
||||
setNotConnected();
|
||||
showSuccess('Настройки сброшены');
|
||||
} else {
|
||||
showError('Не удалось сбросить настройки');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка сброса настроек:', error);
|
||||
showError('Не удалось сбросить настройки');
|
||||
}
|
||||
},
|
||||
buttonStyle: { backgroundColor: APP_COLORS.warning },
|
||||
textStyle: { color: APP_COLORS.white }
|
||||
}
|
||||
]
|
||||
});
|
||||
}, [showInfo, showSuccess, showError, setNotConnected, clearSettings]);
|
||||
|
||||
//Оптимизация базы данных
|
||||
const handleOptimizeDb = React.useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const success = await vacuum();
|
||||
if (success) {
|
||||
showSuccess('База данных оптимизирована');
|
||||
} else {
|
||||
showError('Не удалось оптимизировать базу данных');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка оптимизации БД:', error);
|
||||
showError('Не удалось оптимизировать базу данных');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [vacuum, showSuccess, showError]);
|
||||
|
||||
//Информация о приложении
|
||||
const handleShowAppInfo = React.useCallback(() => {
|
||||
const appInfo = getAppInfo({
|
||||
mode,
|
||||
serverUrl,
|
||||
isDbReady
|
||||
});
|
||||
|
||||
showInfo(appInfo, {
|
||||
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]);
|
||||
|
||||
return (
|
||||
<AdaptiveView padding={false}>
|
||||
<AppHeader showBackButton={true} onBackPress={handleBackPress} showMenuButton={false} />
|
||||
|
||||
<ScrollView style={styles.scrollView} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
||||
<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={({ pressed }) => [styles.serverUrlField, pressed && styles.serverUrlFieldPressed]}
|
||||
onPress={handleOpenServerUrlDialog}
|
||||
disabled={isLoading || !isDbReady}
|
||||
>
|
||||
<AppText
|
||||
style={[styles.serverUrlText, !serverUrl && styles.serverUrlPlaceholder]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{serverUrl || 'Нажмите для ввода адреса'}
|
||||
</AppText>
|
||||
</Pressable>
|
||||
|
||||
{serverUrl ? (
|
||||
<CopyButton
|
||||
value={serverUrl}
|
||||
onCopy={handleCopyServerUrl}
|
||||
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="О приложении" 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
|
||||
visible={isServerUrlDialogVisible}
|
||||
title="Адрес сервера"
|
||||
label="URL сервера приложений"
|
||||
value={serverUrl}
|
||||
placeholder="https://example.com/api"
|
||||
keyboardType="url"
|
||||
confirmText="Сохранить"
|
||||
cancelText="Отмена"
|
||||
onConfirm={handleSaveServerUrl}
|
||||
onCancel={handleCloseServerUrlDialog}
|
||||
validator={validateServerUrl}
|
||||
/>
|
||||
</AdaptiveView>
|
||||
);
|
||||
}
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = SettingsScreen;
|
||||
33
rn/app/src/styles/common/AdaptiveView.styles.js
Normal file
33
rn/app/src/styles/common/AdaptiveView.styles.js
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Стили адаптивного контейнера
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const { StyleSheet } = require('react-native'); //StyleSheet React Native
|
||||
const { APP_COLORS } = require('../../config/theme'); //Цветовая схема приложения
|
||||
const { UI } = require('../../config/appConfig'); //Конфигурация UI
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Стили адаптивного контейнера
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: APP_COLORS.background
|
||||
},
|
||||
padding: {
|
||||
paddingHorizontal: UI.PADDING
|
||||
}
|
||||
});
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = styles;
|
||||
58
rn/app/src/styles/common/AppButton.styles.js
Normal file
58
rn/app/src/styles/common/AppButton.styles.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Стили общего компонента кнопки
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const { StyleSheet } = require('react-native'); //StyleSheet React Native
|
||||
const { APP_COLORS } = require('../../config/theme'); //Цветовая схема приложения
|
||||
const { UI } = require('../../config/appConfig'); //Конфигурация UI
|
||||
const { responsiveSpacing } = require('../../utils/responsive'); //Адаптивные утилиты
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Стили кнопки
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
borderRadius: UI.BORDER_RADIUS,
|
||||
backgroundColor: APP_COLORS.primary,
|
||||
overflow: 'hidden',
|
||||
minHeight: UI.BUTTON_HEIGHT,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
pressed: {
|
||||
opacity: 0.85,
|
||||
transform: [{ scale: 0.98 }]
|
||||
},
|
||||
disabled: {
|
||||
backgroundColor: APP_COLORS.primaryLight,
|
||||
opacity: 0.6
|
||||
},
|
||||
content: {
|
||||
paddingHorizontal: responsiveSpacing(4),
|
||||
paddingVertical: responsiveSpacing(2),
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
text: {
|
||||
color: APP_COLORS.white,
|
||||
fontWeight: '600',
|
||||
fontSize: UI.FONT_SIZE_MD,
|
||||
textAlign: 'center',
|
||||
includeFontPadding: false,
|
||||
textAlignVertical: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = styles;
|
||||
73
rn/app/src/styles/common/AppInput.styles.js
Normal file
73
rn/app/src/styles/common/AppInput.styles.js
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Стили компонента ввода
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const { StyleSheet } = require('react-native'); //StyleSheet React Native
|
||||
const { APP_COLORS } = require('../../config/theme'); //Цветовая схема приложения
|
||||
const { UI } = require('../../config/appConfig'); //Конфигурация UI
|
||||
const { responsiveSpacing } = require('../../utils/responsive'); //Адаптивные утилиты
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Стили компонента ввода
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginBottom: responsiveSpacing(4)
|
||||
},
|
||||
label: {
|
||||
marginBottom: responsiveSpacing(2),
|
||||
color: APP_COLORS.textPrimary
|
||||
},
|
||||
input: {
|
||||
borderWidth: 1,
|
||||
borderColor: APP_COLORS.borderMedium,
|
||||
borderRadius: UI.BORDER_RADIUS,
|
||||
paddingHorizontal: responsiveSpacing(3),
|
||||
paddingVertical: responsiveSpacing(2.5),
|
||||
fontSize: UI.FONT_SIZE_MD,
|
||||
color: APP_COLORS.textPrimary,
|
||||
backgroundColor: APP_COLORS.white,
|
||||
minHeight: UI.INPUT_HEIGHT,
|
||||
includeFontPadding: false,
|
||||
textAlignVertical: 'center'
|
||||
},
|
||||
inputFocused: {
|
||||
borderColor: APP_COLORS.primary,
|
||||
borderWidth: 2,
|
||||
shadowColor: APP_COLORS.primary,
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 4,
|
||||
elevation: 2
|
||||
},
|
||||
inputError: {
|
||||
borderColor: APP_COLORS.error
|
||||
},
|
||||
inputDisabled: {
|
||||
backgroundColor: APP_COLORS.surfaceAlt,
|
||||
color: APP_COLORS.textTertiary
|
||||
},
|
||||
placeholder: {
|
||||
color: APP_COLORS.textTertiary
|
||||
},
|
||||
helperText: {
|
||||
marginTop: responsiveSpacing(1),
|
||||
color: APP_COLORS.textSecondary
|
||||
},
|
||||
helperTextError: {
|
||||
color: APP_COLORS.error
|
||||
}
|
||||
});
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = styles;
|
||||
135
rn/app/src/styles/common/AppLogo.styles.js
Normal file
135
rn/app/src/styles/common/AppLogo.styles.js
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
Предрейсовые осмотры - мобильное приложение
|
||||
Стили компонента иконки/логотипа приложения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
const { StyleSheet } = require('react-native'); //StyleSheet React Native
|
||||
const { responsiveSize } = require('../../utils/responsive'); //Адаптивные утилиты
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Цвета иконки
|
||||
const TEAL_BACKGROUND = '#50AF95';
|
||||
const ROBOT_WHITE = '#FFFFFF';
|
||||
|
||||
//Размеры для разных вариантов
|
||||
const SIZES = {
|
||||
small: responsiveSize(32),
|
||||
medium: responsiveSize(40),
|
||||
large: responsiveSize(56)
|
||||
};
|
||||
|
||||
//Стили логотипа
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: TEAL_BACKGROUND,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
containerSmall: {
|
||||
width: SIZES.small,
|
||||
height: SIZES.small,
|
||||
borderRadius: responsiveSize(6)
|
||||
},
|
||||
containerMedium: {
|
||||
width: SIZES.medium,
|
||||
height: SIZES.medium,
|
||||
borderRadius: responsiveSize(8)
|
||||
},
|
||||
containerLarge: {
|
||||
width: SIZES.large,
|
||||
height: SIZES.large,
|
||||
borderRadius: responsiveSize(10)
|
||||
},
|
||||
robotContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
antennasContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
marginBottom: responsiveSize(-1)
|
||||
},
|
||||
antenna: {
|
||||
backgroundColor: ROBOT_WHITE,
|
||||
borderRadius: responsiveSize(2)
|
||||
},
|
||||
antennaLeft: {
|
||||
transform: [{ rotate: '-30deg' }],
|
||||
marginRight: responsiveSize(4)
|
||||
},
|
||||
antennaRight: {
|
||||
transform: [{ rotate: '30deg' }],
|
||||
marginLeft: responsiveSize(4)
|
||||
},
|
||||
antennaSmall: {
|
||||
width: responsiveSize(1.5),
|
||||
height: responsiveSize(4)
|
||||
},
|
||||
antennaMedium: {
|
||||
width: responsiveSize(2),
|
||||
height: responsiveSize(5)
|
||||
},
|
||||
antennaLarge: {
|
||||
width: responsiveSize(2.5),
|
||||
height: responsiveSize(7)
|
||||
},
|
||||
head: {
|
||||
backgroundColor: ROBOT_WHITE,
|
||||
borderTopLeftRadius: responsiveSize(100),
|
||||
borderTopRightRadius: responsiveSize(100),
|
||||
borderBottomLeftRadius: responsiveSize(4),
|
||||
borderBottomRightRadius: responsiveSize(4),
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
headSmall: {
|
||||
width: responsiveSize(18),
|
||||
height: responsiveSize(10),
|
||||
paddingTop: responsiveSize(3)
|
||||
},
|
||||
headMedium: {
|
||||
width: responsiveSize(22),
|
||||
height: responsiveSize(12),
|
||||
paddingTop: responsiveSize(4)
|
||||
},
|
||||
headLarge: {
|
||||
width: responsiveSize(32),
|
||||
height: responsiveSize(18),
|
||||
paddingTop: responsiveSize(5)
|
||||
},
|
||||
eyesContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
width: '50%'
|
||||
},
|
||||
eye: {
|
||||
backgroundColor: TEAL_BACKGROUND,
|
||||
borderRadius: responsiveSize(100)
|
||||
},
|
||||
eyeSmall: {
|
||||
width: responsiveSize(2),
|
||||
height: responsiveSize(2)
|
||||
},
|
||||
eyeMedium: {
|
||||
width: responsiveSize(2.5),
|
||||
height: responsiveSize(2.5)
|
||||
},
|
||||
eyeLarge: {
|
||||
width: responsiveSize(4),
|
||||
height: responsiveSize(4)
|
||||
}
|
||||
});
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
module.exports = styles;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user