Инициализация директории приложения, формирование структуры и основных компонентов приложения
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