init commit
6
.buckconfig
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
2
.bundle/config
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
BUNDLE_PATH: "vendor/bundle"
|
||||
BUNDLE_FORCE_RUBY_PLATFORM: 1
|
||||
4
.eslintrc.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: '@react-native-community',
|
||||
};
|
||||
66
.flowconfig
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore polyfills
|
||||
node_modules/react-native/Libraries/polyfills/.*
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/Libraries/Utilities/LoadingView.js
|
||||
|
||||
.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$
|
||||
|
||||
[untyped]
|
||||
.*/node_modules/@react-native-community/cli/.*/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/interface.js
|
||||
node_modules/react-native/flow/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
exact_by_default=true
|
||||
|
||||
format.bracket_spacing=false
|
||||
|
||||
module.file_ext=.js
|
||||
module.file_ext=.json
|
||||
module.file_ext=.ios.js
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
|
||||
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
[lints]
|
||||
sketchy-null-number=warn
|
||||
sketchy-null-mixed=warn
|
||||
sketchy-number=warn
|
||||
untyped-type-import=warn
|
||||
nonstrict-import=warn
|
||||
deprecated-type=warn
|
||||
unsafe-getters-setters=warn
|
||||
unnecessary-invariant=warn
|
||||
|
||||
[strict]
|
||||
deprecated-type
|
||||
nonstrict-import
|
||||
sketchy-null
|
||||
unclear-type
|
||||
unsafe-getters-setters
|
||||
untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.182.0
|
||||
64
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
ios/.xcode.env.local
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
.cxx/
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/
|
||||
|
||||
**/fastlane/report.xml
|
||||
**/fastlane/Preview.html
|
||||
**/fastlane/screenshots
|
||||
**/fastlane/test_output
|
||||
|
||||
# Bundle artifact
|
||||
*.jsbundle
|
||||
|
||||
# Ruby / CocoaPods
|
||||
/ios/Pods/
|
||||
/vendor/bundle/
|
||||
1
.node-version
Normal file
|
|
@ -0,0 +1 @@
|
|||
16
|
||||
7
.prettierrc.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
arrowParens: 'avoid',
|
||||
bracketSameLine: true,
|
||||
bracketSpacing: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
};
|
||||
1
.ruby-version
Normal file
|
|
@ -0,0 +1 @@
|
|||
2.7.5
|
||||
1
.watchmanconfig
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
6
Gemfile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby '2.7.5'
|
||||
|
||||
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
|
||||
14
__tests__/App-test.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import App from '../App';
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
it('renders correctly', () => {
|
||||
renderer.create(<App />);
|
||||
});
|
||||
55
android/app/_BUCK
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
|
||||
|
||||
lib_deps = []
|
||||
|
||||
create_aar_targets(glob(["libs/*.aar"]))
|
||||
|
||||
create_jar_targets(glob(["libs/*.jar"]))
|
||||
|
||||
android_library(
|
||||
name = "all-libs",
|
||||
exported_deps = lib_deps,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "app-code",
|
||||
srcs = glob([
|
||||
"src/main/java/**/*.java",
|
||||
]),
|
||||
deps = [
|
||||
":all-libs",
|
||||
":build_config",
|
||||
":res",
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = "build_config",
|
||||
package = "com.awesomeproject",
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "com.awesomeproject",
|
||||
res = "src/main/res",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//android/keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
package_type = "debug",
|
||||
deps = [
|
||||
":app-code",
|
||||
],
|
||||
)
|
||||
322
android/app/build.gradle
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
apply plugin: "com.android.application"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation. If none specified and
|
||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: true, // clean and rebuild if changing
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* 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 = 'org.webkit:android-jsc:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and that value will be read here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
|
||||
/**
|
||||
* Architectures to build native code for.
|
||||
*/
|
||||
def reactNativeArchitectures() {
|
||||
def value = project.getProperties().get("reactNativeArchitectures")
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.awesomeproject"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// We configure the CMake build only if you decide to opt-in for the New Architecture.
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DPROJECT_BUILD_DIR=$buildDir",
|
||||
"-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
|
||||
"-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
|
||||
"-DNODE_MODULES_DIR=$rootDir/../node_modules",
|
||||
"-DANDROID_STL=c++_shared"
|
||||
}
|
||||
}
|
||||
if (!enableSeparateBuildPerCPUArchitecture) {
|
||||
ndk {
|
||||
abiFilters (*reactNativeArchitectures())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// We configure the NDK build only if you decide to opt-in for the New Architecture.
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "$projectDir/src/main/jni/CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
|
||||
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
|
||||
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
|
||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||
into("$buildDir/react-ndk/exported")
|
||||
}
|
||||
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
|
||||
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
|
||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||
into("$buildDir/react-ndk/exported")
|
||||
}
|
||||
afterEvaluate {
|
||||
// If you wish to add a custom TurboModule or component locally,
|
||||
// you should uncomment this line.
|
||||
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
|
||||
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
|
||||
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
|
||||
|
||||
// Due to a bug inside AGP, we have to explicitly set a dependency
|
||||
// between configureCMakeDebug* tasks and the preBuild tasks.
|
||||
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
|
||||
configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
|
||||
configureCMakeDebug.dependsOn(preDebugBuild)
|
||||
reactNativeArchitectures().each { architecture ->
|
||||
tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
|
||||
dependsOn("preDebugBuild")
|
||||
}
|
||||
tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
|
||||
dependsOn("preReleaseBuild")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include (*reactNativeArchitectures())
|
||||
}
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst 'lib/x86/libc++_shared.so'
|
||||
pickFirst 'lib/x86_64/libjsc.so'
|
||||
pickFirst 'lib/arm64-v8a/libjsc.so'
|
||||
pickFirst 'lib/arm64-v8a/libc++_shared.so'
|
||||
pickFirst 'lib/x86_64/libc++_shared.so'
|
||||
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||
}
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
|
||||
if (enableHermes) {
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// If new architecture is enabled, we let you build RN from source
|
||||
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
|
||||
// This will be applied to all the imported transtitive dependency.
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(module("com.facebook.react:react-native"))
|
||||
.using(project(":ReactAndroid"))
|
||||
.because("On New Architecture we're building React Native from source")
|
||||
substitute(module("com.facebook.react:hermes-engine"))
|
||||
.using(project(":ReactAndroid:hermes-engine"))
|
||||
.because("On New Architecture we're building Hermes from source")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.implementation
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
def isNewArchitectureEnabled() {
|
||||
// To opt-in for the New Architecture, you can either:
|
||||
// - Set `newArchEnabled` to true inside the `gradle.properties` file
|
||||
// - Invoke gradle with `-newArchEnabled=true`
|
||||
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
|
||||
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
||||
}
|
||||
19
android/app/build_defs.bzl
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
"""Helper definitions to glob .aar and .jar targets"""
|
||||
|
||||
def create_aar_targets(aarfiles):
|
||||
for aarfile in aarfiles:
|
||||
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||
lib_deps.append(":" + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
def create_jar_targets(jarfiles):
|
||||
for jarfile in jarfiles:
|
||||
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||
lib_deps.append(":" + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
BIN
android/app/debug.keystore
Normal file
10
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:
|
||||
13
android/app/src/debug/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="28"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.awesomeproject;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceEventListener;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
|
||||
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||
NetworkingModule.setCustomClientBuilder(
|
||||
new NetworkingModule.CustomClientBuilder() {
|
||||
@Override
|
||||
public void apply(OkHttpClient.Builder builder) {
|
||||
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||
}
|
||||
});
|
||||
client.addPlugin(networkFlipperPlugin);
|
||||
client.start();
|
||||
|
||||
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||
// Hence we run if after all native modules have been initialized
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
reactInstanceManager.addReactInstanceEventListener(
|
||||
new ReactInstanceEventListener() {
|
||||
@Override
|
||||
public void onReactContextInitialized(ReactContext reactContext) {
|
||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
android/app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.awesomeproject">
|
||||
|
||||
<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">
|
||||
<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>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.awesomeproject;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
||||
* rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "AwesomeProject";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
|
||||
* you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
|
||||
* (Paper).
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new MainActivityDelegate(this, getMainComponentName());
|
||||
}
|
||||
|
||||
public static class MainActivityDelegate extends ReactActivityDelegate {
|
||||
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
|
||||
super(activity, mainComponentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactRootView createRootView() {
|
||||
ReactRootView reactRootView = new ReactRootView(getContext());
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
|
||||
return reactRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConcurrentRootEnabled() {
|
||||
// If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
|
||||
// More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package com.awesomeproject;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.awesomeproject.newarchitecture.MainApplicationReactNativeHost;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new ReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
};
|
||||
|
||||
private final ReactNativeHost mNewArchitectureNativeHost =
|
||||
new MainApplicationReactNativeHost(this);
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
return mNewArchitectureNativeHost;
|
||||
} else {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// If you opted-in for the New Architecture, we enable the TurboModule system
|
||||
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
*
|
||||
* @param context
|
||||
* @param reactInstanceManager
|
||||
*/
|
||||
private static void initializeFlipper(
|
||||
Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
/*
|
||||
We use reflection here to pick up the class that initializes Flipper,
|
||||
since Flipper library is not available in release mode
|
||||
*/
|
||||
Class<?> aClass = Class.forName("com.awesomeproject.ReactNativeFlipper");
|
||||
aClass
|
||||
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
|
||||
.invoke(null, context, reactInstanceManager);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
package com.awesomeproject.newarchitecture;
|
||||
|
||||
import android.app.Application;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
|
||||
import com.facebook.react.bridge.JSIModulePackage;
|
||||
import com.facebook.react.bridge.JSIModuleProvider;
|
||||
import com.facebook.react.bridge.JSIModuleSpec;
|
||||
import com.facebook.react.bridge.JSIModuleType;
|
||||
import com.facebook.react.bridge.JavaScriptContextHolder;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.UIManager;
|
||||
import com.facebook.react.fabric.ComponentFactory;
|
||||
import com.facebook.react.fabric.CoreComponentsRegistry;
|
||||
import com.facebook.react.fabric.FabricJSIModuleProvider;
|
||||
import com.facebook.react.fabric.ReactNativeConfig;
|
||||
import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||
import com.awesomeproject.BuildConfig;
|
||||
import com.awesomeproject.newarchitecture.components.MainComponentsRegistry;
|
||||
import com.awesomeproject.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
|
||||
* TurboModule delegates and the Fabric Renderer.
|
||||
*
|
||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
||||
* `newArchEnabled` property). Is ignored otherwise.
|
||||
*/
|
||||
public class MainApplicationReactNativeHost extends ReactNativeHost {
|
||||
public MainApplicationReactNativeHost(Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
|
||||
// packages.add(new TurboReactPackage() { ... });
|
||||
// If you have custom Fabric Components, their ViewManagers should also be loaded here
|
||||
// inside a ReactPackage.
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ReactPackageTurboModuleManagerDelegate.Builder
|
||||
getReactPackageTurboModuleManagerDelegateBuilder() {
|
||||
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
|
||||
// for the new architecture and to use TurboModules correctly.
|
||||
return new MainApplicationTurboModuleManagerDelegate.Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSIModulePackage getJSIModulePackage() {
|
||||
return new JSIModulePackage() {
|
||||
@Override
|
||||
public List<JSIModuleSpec> getJSIModules(
|
||||
final ReactApplicationContext reactApplicationContext,
|
||||
final JavaScriptContextHolder jsContext) {
|
||||
final List<JSIModuleSpec> specs = new ArrayList<>();
|
||||
|
||||
// Here we provide a new JSIModuleSpec that will be responsible of providing the
|
||||
// custom Fabric Components.
|
||||
specs.add(
|
||||
new JSIModuleSpec() {
|
||||
@Override
|
||||
public JSIModuleType getJSIModuleType() {
|
||||
return JSIModuleType.UIManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
|
||||
final ComponentFactory componentFactory = new ComponentFactory();
|
||||
CoreComponentsRegistry.register(componentFactory);
|
||||
|
||||
// Here we register a Components Registry.
|
||||
// The one that is generated with the template contains no components
|
||||
// and just provides you the one from React Native core.
|
||||
MainComponentsRegistry.register(componentFactory);
|
||||
|
||||
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
|
||||
|
||||
ViewManagerRegistry viewManagerRegistry =
|
||||
new ViewManagerRegistry(
|
||||
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
|
||||
|
||||
return new FabricJSIModuleProvider(
|
||||
reactApplicationContext,
|
||||
componentFactory,
|
||||
ReactNativeConfig.DEFAULT_CONFIG,
|
||||
viewManagerRegistry);
|
||||
}
|
||||
});
|
||||
return specs;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.awesomeproject.newarchitecture.components;
|
||||
|
||||
import com.facebook.jni.HybridData;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.fabric.ComponentFactory;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
/**
|
||||
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
|
||||
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
|
||||
* folder for you).
|
||||
*
|
||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
||||
* `newArchEnabled` property). Is ignored otherwise.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class MainComponentsRegistry {
|
||||
static {
|
||||
SoLoader.loadLibrary("fabricjni");
|
||||
}
|
||||
|
||||
@DoNotStrip private final HybridData mHybridData;
|
||||
|
||||
@DoNotStrip
|
||||
private native HybridData initHybrid(ComponentFactory componentFactory);
|
||||
|
||||
@DoNotStrip
|
||||
private MainComponentsRegistry(ComponentFactory componentFactory) {
|
||||
mHybridData = initHybrid(componentFactory);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
|
||||
return new MainComponentsRegistry(componentFactory);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.awesomeproject.newarchitecture.modules;
|
||||
|
||||
import com.facebook.jni.HybridData;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class responsible to load the TurboModules. This class has native methods and needs a
|
||||
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
|
||||
* folder for you).
|
||||
*
|
||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
||||
* `newArchEnabled` property). Is ignored otherwise.
|
||||
*/
|
||||
public class MainApplicationTurboModuleManagerDelegate
|
||||
extends ReactPackageTurboModuleManagerDelegate {
|
||||
|
||||
private static volatile boolean sIsSoLibraryLoaded;
|
||||
|
||||
protected MainApplicationTurboModuleManagerDelegate(
|
||||
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
|
||||
super(reactApplicationContext, packages);
|
||||
}
|
||||
|
||||
protected native HybridData initHybrid();
|
||||
|
||||
native boolean canCreateTurboModule(String moduleName);
|
||||
|
||||
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
|
||||
protected MainApplicationTurboModuleManagerDelegate build(
|
||||
ReactApplicationContext context, List<ReactPackage> packages) {
|
||||
return new MainApplicationTurboModuleManagerDelegate(context, packages);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void maybeLoadOtherSoLibraries() {
|
||||
if (!sIsSoLibraryLoaded) {
|
||||
// If you change the name of your application .so file in the Android.mk file,
|
||||
// make sure you update the name here as well.
|
||||
SoLoader.loadLibrary("awesomeproject_appmodules");
|
||||
sIsSoLibraryLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
android/app/src/main/jni/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
# Define the library name here.
|
||||
project(awesomeproject_appmodules)
|
||||
|
||||
# This file includes all the necessary to let you build your application with the New Architecture.
|
||||
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
|
||||
32
android/app/src/main/jni/MainApplicationModuleProvider.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include "MainApplicationModuleProvider.h"
|
||||
|
||||
#include <rncli.h>
|
||||
#include <rncore.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
|
||||
const std::string &moduleName,
|
||||
const JavaTurboModule::InitParams ¶ms) {
|
||||
// Here you can provide your own module provider for TurboModules coming from
|
||||
// either your application or from external libraries. The approach to follow
|
||||
// is similar to the following (for a library called `samplelibrary`:
|
||||
//
|
||||
// auto module = samplelibrary_ModuleProvider(moduleName, params);
|
||||
// if (module != nullptr) {
|
||||
// return module;
|
||||
// }
|
||||
// return rncore_ModuleProvider(moduleName, params);
|
||||
|
||||
// Module providers autolinked by RN CLI
|
||||
auto rncli_module = rncli_ModuleProvider(moduleName, params);
|
||||
if (rncli_module != nullptr) {
|
||||
return rncli_module;
|
||||
}
|
||||
|
||||
return rncore_ModuleProvider(moduleName, params);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
16
android/app/src/main/jni/MainApplicationModuleProvider.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <ReactCommon/JavaTurboModule.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
|
||||
const std::string &moduleName,
|
||||
const JavaTurboModule::InitParams ¶ms);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#include "MainApplicationTurboModuleManagerDelegate.h"
|
||||
#include "MainApplicationModuleProvider.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
|
||||
MainApplicationTurboModuleManagerDelegate::initHybrid(
|
||||
jni::alias_ref<jhybridobject>) {
|
||||
return makeCxxInstance();
|
||||
}
|
||||
|
||||
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod(
|
||||
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
|
||||
makeNativeMethod(
|
||||
"canCreateTurboModule",
|
||||
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule>
|
||||
MainApplicationTurboModuleManagerDelegate::getTurboModule(
|
||||
const std::string &name,
|
||||
const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
// Not implemented yet: provide pure-C++ NativeModules here.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule>
|
||||
MainApplicationTurboModuleManagerDelegate::getTurboModule(
|
||||
const std::string &name,
|
||||
const JavaTurboModule::InitParams ¶ms) {
|
||||
return MainApplicationModuleProvider(name, params);
|
||||
}
|
||||
|
||||
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
|
||||
const std::string &name) {
|
||||
return getTurboModule(name, nullptr) != nullptr ||
|
||||
getTurboModule(name, {.moduleName = name}) != nullptr;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <ReactCommon/TurboModuleManagerDelegate.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class MainApplicationTurboModuleManagerDelegate
|
||||
: public jni::HybridClass<
|
||||
MainApplicationTurboModuleManagerDelegate,
|
||||
TurboModuleManagerDelegate> {
|
||||
public:
|
||||
// Adapt it to the package you used for your Java class.
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/awesomeproject/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
|
||||
|
||||
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
|
||||
|
||||
static void registerNatives();
|
||||
|
||||
std::shared_ptr<TurboModule> getTurboModule(
|
||||
const std::string &name,
|
||||
const std::shared_ptr<CallInvoker> &jsInvoker) override;
|
||||
std::shared_ptr<TurboModule> getTurboModule(
|
||||
const std::string &name,
|
||||
const JavaTurboModule::InitParams ¶ms) override;
|
||||
|
||||
/**
|
||||
* Test-only method. Allows user to verify whether a TurboModule can be
|
||||
* created by instances of this class.
|
||||
*/
|
||||
bool canCreateTurboModule(const std::string &name);
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
65
android/app/src/main/jni/MainComponentsRegistry.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include "MainComponentsRegistry.h"
|
||||
|
||||
#include <CoreComponentsRegistry.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
||||
#include <react/renderer/components/rncore/ComponentDescriptors.h>
|
||||
#include <rncli.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
|
||||
|
||||
std::shared_ptr<ComponentDescriptorProviderRegistry const>
|
||||
MainComponentsRegistry::sharedProviderRegistry() {
|
||||
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
|
||||
|
||||
// Autolinked providers registered by RN CLI
|
||||
rncli_registerProviders(providerRegistry);
|
||||
|
||||
// Custom Fabric Components go here. You can register custom
|
||||
// components coming from your App or from 3rd party libraries here.
|
||||
//
|
||||
// providerRegistry->add(concreteComponentDescriptorProvider<
|
||||
// AocViewerComponentDescriptor>());
|
||||
return providerRegistry;
|
||||
}
|
||||
|
||||
jni::local_ref<MainComponentsRegistry::jhybriddata>
|
||||
MainComponentsRegistry::initHybrid(
|
||||
jni::alias_ref<jclass>,
|
||||
ComponentFactory *delegate) {
|
||||
auto instance = makeCxxInstance(delegate);
|
||||
|
||||
auto buildRegistryFunction =
|
||||
[](EventDispatcher::Weak const &eventDispatcher,
|
||||
ContextContainer::Shared const &contextContainer)
|
||||
-> ComponentDescriptorRegistry::Shared {
|
||||
auto registry = MainComponentsRegistry::sharedProviderRegistry()
|
||||
->createComponentDescriptorRegistry(
|
||||
{eventDispatcher, contextContainer});
|
||||
|
||||
auto mutableRegistry =
|
||||
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
|
||||
|
||||
mutableRegistry->setFallbackComponentDescriptor(
|
||||
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
|
||||
ComponentDescriptorParameters{
|
||||
eventDispatcher, contextContainer, nullptr}));
|
||||
|
||||
return registry;
|
||||
};
|
||||
|
||||
delegate->buildRegistryFunction = buildRegistryFunction;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void MainComponentsRegistry::registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
32
android/app/src/main/jni/MainComponentsRegistry.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <ComponentFactory.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class MainComponentsRegistry
|
||||
: public facebook::jni::HybridClass<MainComponentsRegistry> {
|
||||
public:
|
||||
// Adapt it to the package you used for your Java class.
|
||||
constexpr static auto kJavaDescriptor =
|
||||
"Lcom/awesomeproject/newarchitecture/components/MainComponentsRegistry;";
|
||||
|
||||
static void registerNatives();
|
||||
|
||||
MainComponentsRegistry(ComponentFactory *delegate);
|
||||
|
||||
private:
|
||||
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
|
||||
sharedProviderRegistry();
|
||||
|
||||
static jni::local_ref<jhybriddata> initHybrid(
|
||||
jni::alias_ref<jclass>,
|
||||
ComponentFactory *delegate);
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
11
android/app/src/main/jni/OnLoad.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include <fbjni/fbjni.h>
|
||||
#include "MainApplicationTurboModuleManagerDelegate.h"
|
||||
#include "MainComponentsRegistry.h"
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
||||
return facebook::jni::initialize(vm, [] {
|
||||
facebook::react::MainApplicationTurboModuleManagerDelegate::
|
||||
registerNatives();
|
||||
facebook::react::MainComponentsRegistry::registerNatives();
|
||||
});
|
||||
}
|
||||
36
android/app/src/main/res/drawable/rn_edit_text_material.xml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?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>
|
||||
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 9 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
3
android/app/src/main/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">AwesomeProject</string>
|
||||
</resources>
|
||||
9
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>
|
||||
51
android/build.gradle
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 31
|
||||
targetSdkVersion = 31
|
||||
|
||||
if (System.properties['os.arch'] == "aarch64") {
|
||||
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||
ndkVersion = "24.0.8215888"
|
||||
} else {
|
||||
// Otherwise we default to the side-by-side NDK version from AGP.
|
||||
ndkVersion = "21.4.7075529"
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.2.1")
|
||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||
classpath("de.undercouch:gradle-download-task:5.0.1")
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url("$rootDir/../node_modules/react-native/android")
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url("$rootDir/../node_modules/jsc-android/dist")
|
||||
}
|
||||
mavenCentral {
|
||||
// We don't want to fetch react-native from Maven Central as there are
|
||||
// older versions over there.
|
||||
content {
|
||||
excludeGroup "com.facebook.react"
|
||||
}
|
||||
}
|
||||
google()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
40
android/gradle.properties
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# 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
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.125.0
|
||||
|
||||
# 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=false
|
||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
android/gradlew
vendored
Executable file
|
|
@ -0,0 +1,234 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# 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/master/subprojects/plugins/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
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
|
||||
# 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=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# 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
|
||||
which java >/dev/null 2>&1 || 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
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
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
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# 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" "$@"
|
||||
89
android/gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
@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
|
||||
|
||||
@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=.
|
||||
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%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="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!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
11
android/settings.gradle
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
rootProject.name = 'AwesomeProject'
|
||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':app'
|
||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||
|
||||
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
|
||||
include(":ReactAndroid")
|
||||
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
|
||||
include(":ReactAndroid:hermes-engine")
|
||||
project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
|
||||
}
|
||||
4
app.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "AwesomeProject",
|
||||
"displayName": "AwesomeProject"
|
||||
}
|
||||
3
babel.config.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
};
|
||||
9
index.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './src/App';
|
||||
import {name as appName} from './app.json';
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
11
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)
|
||||
700
ios/AwesomeProject.xcodeproj/project.pbxproj
Normal file
|
|
@ -0,0 +1,700 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* AwesomeProjectTests.m */; };
|
||||
0C80B921A6F3F58F76C31292 /* libPods-AwesomeProject.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-AwesomeProject.a */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
7699B88040F8A987B510C191 /* libPods-AwesomeProject-AwesomeProjectTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-AwesomeProject-AwesomeProjectTests.a */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||
remoteInfo = AwesomeProject;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AwesomeProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* AwesomeProjectTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AwesomeProjectTests.m; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* AwesomeProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AwesomeProject.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AwesomeProject/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = AwesomeProject/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = AwesomeProject/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = AwesomeProject/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = AwesomeProject/main.m; sourceTree = "<group>"; };
|
||||
19F6CBCC0A4E27FBF8BF4A61 /* libPods-AwesomeProject-AwesomeProjectTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AwesomeProject-AwesomeProjectTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3B4392A12AC88292D35C810B /* Pods-AwesomeProject.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject.debug.xcconfig"; path = "Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
5709B34CF0A7D63546082F79 /* Pods-AwesomeProject.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject.release.xcconfig"; sourceTree = "<group>"; };
|
||||
5B7EB9410499542E8C5724F5 /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
5DCACB8F33CDC322A6C60F78 /* libPods-AwesomeProject.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AwesomeProject.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = AwesomeProject/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
89C6BE57DB24E9ADA2F236DE /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; 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 */
|
||||
00E356EB1AD99517003FC87E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7699B88040F8A987B510C191 /* libPods-AwesomeProject-AwesomeProjectTests.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0C80B921A6F3F58F76C31292 /* libPods-AwesomeProject.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
00E356EF1AD99517003FC87E /* AwesomeProjectTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
00E356F21AD99517003FC87E /* AwesomeProjectTests.m */,
|
||||
00E356F01AD99517003FC87E /* Supporting Files */,
|
||||
);
|
||||
path = AwesomeProjectTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
00E356F01AD99517003FC87E /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
00E356F11AD99517003FC87E /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
13B07FAE1A68108700A75B9A /* AwesomeProject */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
);
|
||||
name = AwesomeProject;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
5DCACB8F33CDC322A6C60F78 /* libPods-AwesomeProject.a */,
|
||||
19F6CBCC0A4E27FBF8BF4A61 /* libPods-AwesomeProject-AwesomeProjectTests.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* AwesomeProject */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
00E356EF1AD99517003FC87E /* AwesomeProjectTests */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
usesTabs = 0;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* AwesomeProject.app */,
|
||||
00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B4392A12AC88292D35C810B /* Pods-AwesomeProject.debug.xcconfig */,
|
||||
5709B34CF0A7D63546082F79 /* Pods-AwesomeProject.release.xcconfig */,
|
||||
5B7EB9410499542E8C5724F5 /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */,
|
||||
89C6BE57DB24E9ADA2F236DE /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
00E356ED1AD99517003FC87E /* AwesomeProjectTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */;
|
||||
buildPhases = (
|
||||
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
|
||||
00E356EA1AD99517003FC87E /* Sources */,
|
||||
00E356EB1AD99517003FC87E /* Frameworks */,
|
||||
00E356EC1AD99517003FC87E /* Resources */,
|
||||
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
|
||||
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
00E356F51AD99517003FC87E /* PBXTargetDependency */,
|
||||
);
|
||||
name = AwesomeProjectTests;
|
||||
productName = AwesomeProjectTests;
|
||||
productReference = 00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
13B07F861A680F5B00A75B9A /* AwesomeProject */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */;
|
||||
buildPhases = (
|
||||
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
|
||||
FD10A7F022414F080027D42C /* Start Packager */,
|
||||
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 = AwesomeProject;
|
||||
productName = AwesomeProject;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* AwesomeProject.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1210;
|
||||
TargetAttributes = {
|
||||
00E356ED1AD99517003FC87E = {
|
||||
CreatedOnToolsVersion = 6.2;
|
||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1120;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AwesomeProject" */;
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* AwesomeProject */,
|
||||
00E356ED1AD99517003FC87E /* AwesomeProjectTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
00E356EC1AD99517003FC87E /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
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=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/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-AwesomeProject/Pods-AwesomeProject-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
A55EABD7B0C7F3A422A6CC61 /* [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-AwesomeProject-AwesomeProjectTests-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;
|
||||
};
|
||||
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-AwesomeProject-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;
|
||||
};
|
||||
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
FD10A7F022414F080027D42C /* Start Packager */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Start Packager";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
00E356EA1AD99517003FC87E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 13B07F861A680F5B00A75B9A /* AwesomeProject */;
|
||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
00E356F61AD99517003FC87E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = AwesomeProjectTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
"$(inherited)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
00E356F71AD99517003FC87E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
INFOPLIST_FILE = AwesomeProjectTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
"$(inherited)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-AwesomeProject.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = AwesomeProject/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = AwesomeProject;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-AwesomeProject.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = AwesomeProject/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = AwesomeProject;
|
||||
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++17";
|
||||
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 = 12.4;
|
||||
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",
|
||||
);
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
83CBBA211A601CBA00E9B192 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
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 = 12.4;
|
||||
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",
|
||||
);
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
00E356F61AD99517003FC87E /* Debug */,
|
||||
00E356F71AD99517003FC87E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
13B07F941A680F5B00A75B9A /* Debug */,
|
||||
13B07F951A680F5B00A75B9A /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AwesomeProject" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83CBBA201A601CBA00E9B192 /* Debug */,
|
||||
83CBBA211A601CBA00E9B192 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
}
|
||||
|
|
@ -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 = "AwesomeProject.app"
|
||||
BlueprintName = "AwesomeProject"
|
||||
ReferencedContainer = "container:AwesomeProject.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 = "AwesomeProjectTests.xctest"
|
||||
BlueprintName = "AwesomeProjectTests"
|
||||
ReferencedContainer = "container:AwesomeProject.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 = "AwesomeProject.app"
|
||||
BlueprintName = "AwesomeProject"
|
||||
ReferencedContainer = "container:AwesomeProject.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "AwesomeProject.app"
|
||||
BlueprintName = "AwesomeProject"
|
||||
ReferencedContainer = "container:AwesomeProject.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
8
ios/AwesomeProject/AppDelegate.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIWindow *window;
|
||||
|
||||
@end
|
||||
133
ios/AwesomeProject/AppDelegate.mm
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#import <React/RCTAppSetupUtils.h>
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
|
||||
#import <React/RCTSurfacePresenter.h>
|
||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||
|
||||
#import <react/config/ReactNativeConfig.h>
|
||||
|
||||
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
||||
|
||||
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
|
||||
RCTTurboModuleManager *_turboModuleManager;
|
||||
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
}
|
||||
@end
|
||||
#endif
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTAppSetupPrepareApp(application);
|
||||
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
|
||||
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
|
||||
#endif
|
||||
|
||||
NSDictionary *initProps = [self prepareInitialProps];
|
||||
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"AwesomeProject", initProps);
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
} else {
|
||||
rootView.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [UIViewController new];
|
||||
rootViewController.view = rootView;
|
||||
self.window.rootViewController = rootViewController;
|
||||
[self.window makeKeyAndVisible];
|
||||
return YES;
|
||||
}
|
||||
|
||||
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
|
||||
///
|
||||
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
|
||||
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
|
||||
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
|
||||
- (BOOL)concurrentRootEnabled
|
||||
{
|
||||
// Switch this bool to turn on and off the concurrent root
|
||||
return true;
|
||||
}
|
||||
|
||||
- (NSDictionary *)prepareInitialProps
|
||||
{
|
||||
NSMutableDictionary *initProps = [NSMutableDictionary new];
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
|
||||
#endif
|
||||
|
||||
return initProps;
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
||||
#else
|
||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
}
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
|
||||
delegate:self
|
||||
jsInvoker:bridge.jsCallInvoker];
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
|
||||
}
|
||||
|
||||
#pragma mark RCTTurboModuleManagerDelegate
|
||||
|
||||
- (Class)getModuleClassFromName:(const char *)name
|
||||
{
|
||||
return RCTCoreModulesClassProvider(name);
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
||||
initParams:
|
||||
(const facebook::react::ObjCTurboModule::InitParams &)params
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
|
||||
{
|
||||
return RCTAppSetupDefaultModuleFromClass(moduleClass);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
|
@ -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
ios/AwesomeProject/Images.xcassets/Contents.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
55
ios/AwesomeProject/Info.plist
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>AwesomeProject</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>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
47
ios/AwesomeProject/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="AwesomeProject" 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>
|
||||
10
ios/AwesomeProject/main.m
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
66
ios/AwesomeProjectTests/AwesomeProjectTests.m
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#define TIMEOUT_SECONDS 600
|
||||
#define TEXT_TO_LOOK_FOR @"Welcome to React"
|
||||
|
||||
@interface AwesomeProjectTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation AwesomeProjectTests
|
||||
|
||||
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
|
||||
{
|
||||
if (test(view)) {
|
||||
return YES;
|
||||
}
|
||||
for (UIView *subview in [view subviews]) {
|
||||
if ([self findSubviewInView:subview matching:test]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)testRendersWelcomeScreen
|
||||
{
|
||||
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||
BOOL foundElement = NO;
|
||||
|
||||
__block NSString *redboxError = nil;
|
||||
#ifdef DEBUG
|
||||
RCTSetLogFunction(
|
||||
^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||
if (level >= RCTLogLevelError) {
|
||||
redboxError = message;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
|
||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
|
||||
foundElement = [self findSubviewInView:vc.view
|
||||
matching:^BOOL(UIView *view) {
|
||||
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}];
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||
#endif
|
||||
|
||||
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
||||
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
@end
|
||||
24
ios/AwesomeProjectTests/Info.plist
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>en</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>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
43
ios/Podfile
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
|
||||
platform :ios, '12.4'
|
||||
install! 'cocoapods', :deterministic_uuids => false
|
||||
|
||||
target 'AwesomeProject' do
|
||||
config = use_native_modules!
|
||||
|
||||
# Flags change depending on the env values.
|
||||
flags = get_default_flags()
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
# Hermes is now enabled by default. Disable by setting this flag to false.
|
||||
# Upcoming versions of React Native may rely on get_default_flags(), but
|
||||
# we make it explicit here to aid in the React Native upgrade process.
|
||||
:hermes_enabled => true,
|
||||
:fabric_enabled => flags[:fabric_enabled],
|
||||
# Enables Flipper.
|
||||
#
|
||||
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||
# you should disable the next line.
|
||||
:flipper_configuration => FlipperConfiguration.enabled,
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||
)
|
||||
|
||||
target 'AwesomeProjectTests' do
|
||||
inherit! :complete
|
||||
# Pods for testing
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(
|
||||
installer,
|
||||
# Set `mac_catalyst_enabled` to `true` in order to apply patches
|
||||
# necessary for Mac Catalyst builds
|
||||
:mac_catalyst_enabled => false
|
||||
)
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
end
|
||||
end
|
||||
17
metro.config.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Metro configuration for React Native
|
||||
* https://github.com/facebook/react-native
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
transformer: {
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: true,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
38541
package-lock.json
generated
Normal file
53
package.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"name": "AwesomeProject",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios",
|
||||
"start": "react-native start",
|
||||
"test": "jest",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^13.0.0",
|
||||
"@react-native-community/netinfo": "^9.3.2",
|
||||
"@react-navigation/native": "^6.0.13",
|
||||
"@react-navigation/stack": "^6.3.1",
|
||||
"axios": "^0.27.2",
|
||||
"expo-barcode-scanner": "^11.4.0",
|
||||
"expo-file-system": "^14.1.0",
|
||||
"expo-video-player": "^2.1.0",
|
||||
"i18n-js": "^4.1.1",
|
||||
"react": "18.1.0",
|
||||
"react-native": "0.70.1",
|
||||
"react-native-animated-tree-view": "^0.1.4",
|
||||
"react-native-b-crypt-bridge": "^1.0.0",
|
||||
"react-native-blob-util": "^0.16.2",
|
||||
"react-native-elements": "^3.4.2",
|
||||
"react-native-flash-message": "^0.3.1",
|
||||
"react-native-gesture-handler": "^2.6.2",
|
||||
"react-native-pdf": "^6.6.2",
|
||||
"react-native-progress": "^5.0.0",
|
||||
"react-native-splash-screen": "^3.3.0",
|
||||
"react-native-unimodules": "^0.14.10",
|
||||
"react-native-vector-icons": "^9.2.0",
|
||||
"react-redux": "^8.0.4",
|
||||
"realm": "^10.21.1",
|
||||
"redux": "^4.2.0",
|
||||
"vcard-parser": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.9",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@react-native-community/eslint-config": "^2.0.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"eslint": "^7.32.0",
|
||||
"jest": "^26.6.3",
|
||||
"metro-react-native-babel-preset": "^0.72.1",
|
||||
"react-test-renderer": "18.1.0"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native"
|
||||
}
|
||||
}
|
||||
464
src/App.js
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
import 'react-native-gesture-handler';
|
||||
import React from 'react';
|
||||
import {createStore} from 'redux';
|
||||
import {Provider} from 'react-redux';
|
||||
import {NavigationContainer} from '@react-navigation/native';
|
||||
import {createStackNavigator} from '@react-navigation/stack';
|
||||
import {TouchableOpacity, View, Image, Text, TextInput, StyleSheet, Alert} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
|
||||
import FlashMessage, {showMessage} from 'react-native-flash-message';
|
||||
|
||||
import LoginScreen from './Views/Login';
|
||||
import HomeScreen from './Views/Home';
|
||||
import DirectoriesScreen from './Views/Directories';
|
||||
import VideoScreen from './Views/Video';
|
||||
import PDFScreen from './Views/PDF';
|
||||
import SyncScreen from './Views/Sync';
|
||||
import ContactScreen from './Views/Contact';
|
||||
import ContactDetailsScreen from './Views/ContactDetails';
|
||||
import OnlyVideosScreen from './Views/OnlyVideos';
|
||||
import SearchScreen from "./Views/Search";
|
||||
import MySpaceScreen from './Views/MySpace';
|
||||
|
||||
import UsersModel from './Models/Users';
|
||||
import DirectoriesModel from './Models/Directories';
|
||||
import DocumentsModel from './Models/Documents';
|
||||
import ContactsModel from './Models/Contacts';
|
||||
import SendBrochureModel from './Models/SendBrochure';
|
||||
import CommonModel from './Models/Common';
|
||||
|
||||
import Sync from './Controllers/Sync';
|
||||
import Helpers from "./Controllers/Helpers";
|
||||
import i18n from "./I18n";
|
||||
|
||||
const initialState = {
|
||||
locale: 'fr',
|
||||
users: {},
|
||||
directories: {},
|
||||
documents: {},
|
||||
contacts: {},
|
||||
syncVersion: 0,
|
||||
logged: undefined,
|
||||
currentUser: {},
|
||||
currentOpenedDoc: {},
|
||||
ShowIsActive: 0,
|
||||
};
|
||||
|
||||
function reducer(state = initialState, action) {
|
||||
//console.log('reducer', state, action);
|
||||
//console.log('DISPATCH');
|
||||
|
||||
switch (action.type) {
|
||||
case 'INIT':
|
||||
let docs = DocumentsModel.getAll(DirectoriesModel.getAll(
|
||||
state.currentUser.role,
|
||||
state.currentUser.authorizedFolders,
|
||||
false,
|
||||
state.locale
|
||||
));
|
||||
return {
|
||||
...state,
|
||||
users: UsersModel.getAll(),
|
||||
directories: DirectoriesModel.getAll(
|
||||
state.currentUser.role,
|
||||
state.currentUser.authorizedFolders,
|
||||
true,
|
||||
state.locale
|
||||
),
|
||||
documents: docs.filter(item => item.lang === state.locale),
|
||||
contacts: ContactsModel.getByUser(state.currentUser.id),
|
||||
ShowIsActive: CommonModel.get('ShowIsActive')['value'],
|
||||
};
|
||||
case 'INIT_USERS':
|
||||
return {
|
||||
...state,
|
||||
users: UsersModel.getAll(),
|
||||
};
|
||||
case 'SET_CURRENT_USER':
|
||||
return {
|
||||
...state,
|
||||
currentUser: JSON.parse(action.data),
|
||||
};
|
||||
case 'SET_LOGGED':
|
||||
if (action.data === false) {
|
||||
CommonModel.delete('isLogged');
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
logged: action.data,
|
||||
};
|
||||
case 'UPDATE_LOCALE':
|
||||
let nxt;
|
||||
if (action.data === undefined) {
|
||||
const availableLocales = ['fr', 'en', 'de'];
|
||||
if (availableLocales.indexOf(i18n.locale) === availableLocales.length - 1) {
|
||||
nxt = availableLocales[0];
|
||||
} else {
|
||||
nxt = availableLocales[availableLocales.indexOf(i18n.locale) + 1];
|
||||
}
|
||||
} else {
|
||||
nxt = action.data;
|
||||
}
|
||||
i18n.locale = nxt;
|
||||
CommonModel.update({key: 'locale', value: nxt});
|
||||
let docsL = DocumentsModel.getAll(DirectoriesModel.getAll(
|
||||
state.currentUser.role,
|
||||
state.currentUser.authorizedFolders,
|
||||
false,
|
||||
nxt
|
||||
));
|
||||
return {
|
||||
...state,
|
||||
locale: nxt,
|
||||
directories: DirectoriesModel.getAll(
|
||||
state.currentUser.role,
|
||||
state.currentUser.authorizedFolders,
|
||||
true,
|
||||
nxt
|
||||
),
|
||||
documents: docsL.filter(item => item.lang === nxt),
|
||||
};
|
||||
case 'SET_CURRENT_OPENED_DOC':
|
||||
return {
|
||||
...state,
|
||||
currentOpenedDoc: action.data
|
||||
};
|
||||
case 'UPDATE_VERSION':
|
||||
return {
|
||||
...state,
|
||||
syncVersion: action.data,
|
||||
};
|
||||
case 'UPDATE_SHOW_IS_ACTIVE':
|
||||
Sync.updateShowIsActive(action.data);
|
||||
return {
|
||||
...state,
|
||||
ShowIsActive: action.data,
|
||||
};
|
||||
case 'UPDATE_CONTACT':
|
||||
ContactsModel.update(action.data);
|
||||
return {
|
||||
...state,
|
||||
contacts: ContactsModel.getByUser(state.currentUser.id),
|
||||
};
|
||||
case 'ADD_CONTACT':
|
||||
ContactsModel.add(action.data);
|
||||
return {
|
||||
...state,
|
||||
contacts: ContactsModel.getByUser(state.currentUser.id),
|
||||
};
|
||||
case 'DELETE_CONTACT':
|
||||
ContactsModel.delete(action.data);
|
||||
return {
|
||||
...state,
|
||||
contacts: ContactsModel.getByUser(state.currentUser.id),
|
||||
};
|
||||
case 'SEND_BROCHURE':
|
||||
SendBrochureModel.add(action.data);
|
||||
return state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
const store = createStore(reducer);
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
store.dispatch({type: 'INIT_USERS'});
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
logged: false,
|
||||
searchTxt: '',
|
||||
sendEmail: '',
|
||||
ShowIsActive: '0'
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
const unsubscribe = store.subscribe(this.handleChange.bind(this));
|
||||
if (Sync.isInstalled() && Sync.isLogged()) {
|
||||
store.dispatch({
|
||||
type: 'SET_CURRENT_USER',
|
||||
data: CommonModel.get('isLogged').value,
|
||||
});
|
||||
store.dispatch({type: 'SET_LOGGED', data: true});
|
||||
}
|
||||
const savedLocale = CommonModel.get('locale');
|
||||
if (savedLocale !== undefined && savedLocale !== false) {
|
||||
store.dispatch({type: 'UPDATE_LOCALE', data: savedLocale.value});
|
||||
}
|
||||
}
|
||||
|
||||
handleChange() {
|
||||
const logged = store.getState().logged;
|
||||
if (logged === false) {
|
||||
CommonModel.delete('isLogged');
|
||||
}
|
||||
if (logged !== undefined) {
|
||||
this.setState({logged: logged});
|
||||
}
|
||||
this.setState({ShowIsActive: store.getState().ShowIsActive});
|
||||
}
|
||||
|
||||
LogoTitle(title) {
|
||||
return (
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Image
|
||||
style={{ width: 100, height: 50, resizeMode: 'stretch' }}
|
||||
source={require('./assets/img/logo.png')}
|
||||
/>
|
||||
<Text style={{marginLeft: 10, fontSize: 18}}>{title}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
SendEmail() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row', alignItems: 'center', marginRight: 10}}>
|
||||
<TextInput style={styles.searchInput} placeholder={i18n.t('global.email')} value={this.state.sendEmail} onChangeText={(text) => this.setState({sendEmail: text})}/>
|
||||
<TouchableOpacity style={styles.btnSearch} onPress={() => {
|
||||
if (this.state.sendEmail.length > 0) {
|
||||
if (Helpers.isEmailValid(this.state.sendEmail)) {
|
||||
const email = {
|
||||
email: this.state.sendEmail,
|
||||
subject: i18n.t('app.file_sharing'),
|
||||
message: '',
|
||||
documents: JSON.stringify([store.getState().currentOpenedDoc]),
|
||||
user_id: store.getState().currentUser.id
|
||||
};
|
||||
store.dispatch({type: 'SEND_BROCHURE', data: email});
|
||||
showMessage({
|
||||
message: i18n.t('global.message_sent') + '.',
|
||||
description: i18n.t('global.message_will_be_sent_after_sync') + '.',
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
this.setState({sendEmail: ''});
|
||||
} else {
|
||||
Alert.alert(i18n.t('global.warning') + ' !', i18n.t('error.invalid_email_address') + '.', [
|
||||
{text: 'OK'},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<Icon name={'paper-plane'} size={20} color={'white'}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const savedLocale = CommonModel.get('locale');
|
||||
let locale;
|
||||
if (savedLocale !== undefined) {
|
||||
locale = savedLocale.value;
|
||||
} else {
|
||||
locale = store.getState().locale;
|
||||
}
|
||||
let flag;
|
||||
switch (locale) {
|
||||
case "fr":
|
||||
flag = require('./assets/img/flags/fr.png');
|
||||
break;
|
||||
case "en":
|
||||
flag = require('./assets/img/flags/en.png');
|
||||
break;
|
||||
case "de":
|
||||
flag = require('./assets/img/flags/de.png');
|
||||
break;
|
||||
case "es":
|
||||
flag = require('./assets/img/flags/es.png');
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator>
|
||||
{this.state.logged === false ? (
|
||||
<Stack.Screen
|
||||
name="LoginScreen"
|
||||
component={LoginScreen}
|
||||
options={(navigation, route) => ({
|
||||
headerTitle: () => this.LogoTitle(i18n.t('global.login')),
|
||||
headerRight: () => (
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
store.dispatch({type: 'UPDATE_LOCALE'});
|
||||
this.setState({ state: this.state }); // to force re-rendering
|
||||
}}
|
||||
style={{marginRight: 15}}>
|
||||
<Image source={flag} style={{width: 25, height: 25}}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
),
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
<Stack.Screen
|
||||
name="HomeScreen"
|
||||
component={HomeScreen}
|
||||
options={(navigation, route) => ({
|
||||
headerTitle: () => this.LogoTitle(i18n.t('global.home')),
|
||||
headerRight: () => (
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<View style={{flexDirection: 'row', marginRight: 50}}>
|
||||
<TextInput style={styles.searchInput} placeholder={i18n.t('global.search')} value={this.state.searchTxt} onChangeText={(text) => this.setState({searchTxt: text})}/>
|
||||
<TouchableOpacity style={styles.btnSearch} onPress={() => {
|
||||
if (this.state.searchTxt.length > 0) {
|
||||
navigation.navigation.navigate('SearchScreen', {
|
||||
needle: this.state.searchTxt,
|
||||
onlyVideos: false,
|
||||
});
|
||||
this.setState({searchTxt: ''});
|
||||
}
|
||||
}}>
|
||||
<Icon name={'search'} size={20} color={'white'}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
store.dispatch({type: 'UPDATE_LOCALE'});
|
||||
}}
|
||||
style={{marginRight: 15}}>
|
||||
<Image source={flag} style={{width: 25, height: 25}}/>
|
||||
</TouchableOpacity>
|
||||
{this.state.ShowIsActive === '1' && (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.navigation.navigate('MySpaceScreen')}
|
||||
style={{marginRight: 15}}>
|
||||
<Icon name="user-circle" size={25} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
onPress={() =>
|
||||
navigation.navigation.navigate('SyncScreen', {
|
||||
isInstalled: true,
|
||||
})
|
||||
}
|
||||
style={{marginRight: 15}}>
|
||||
<Icon name="sync-alt" size={20} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
store.dispatch({type: 'SET_LOGGED', data: false});
|
||||
}}
|
||||
style={{marginRight: 15}}>
|
||||
<Icon name="sign-out-alt" size={20} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
),
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<Stack.Screen
|
||||
name="SyncScreen"
|
||||
component={SyncScreen}
|
||||
options={{headerTitle: () => this.LogoTitle(i18n.t('global.synchronization'))}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="DirectoriesScreen"
|
||||
component={DirectoriesScreen}
|
||||
options={{headerTitle: () => this.LogoTitle(i18n.t('app.folders'))}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="OnlyVideosScreen"
|
||||
component={OnlyVideosScreen}
|
||||
options={(navigation, route) => ({
|
||||
headerTitle: () => this.LogoTitle(i18n.t('global.videos')),
|
||||
headerRight: () => (
|
||||
<View style={{flexDirection: 'row', marginRight: 5}}>
|
||||
<TextInput style={styles.searchInput} placeholder={i18n.t('global.search')} value={this.state.searchTxt} onChangeText={(text) => this.setState({searchTxt: text})}/>
|
||||
<TouchableOpacity style={styles.btnSearch} onPress={() => {
|
||||
if (this.state.searchTxt.length > 0) {
|
||||
navigation.navigation.navigate('SearchScreen', {
|
||||
needle: this.state.searchTxt,
|
||||
onlyVideos: true,
|
||||
});
|
||||
this.setState({searchTxt: ''});
|
||||
}
|
||||
}}>
|
||||
<Icon name={'search'} size={20} color={'white'}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
),
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="VideoScreen"
|
||||
component={VideoScreen}
|
||||
options={(navigation, route) => ({
|
||||
headerTitle: () => this.LogoTitle(i18n.t('app.video')),
|
||||
headerRight: () => this.SendEmail()
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="PDFScreen"
|
||||
component={PDFScreen}
|
||||
options={(navigation, route) => ({
|
||||
headerTitle: () => this.LogoTitle('PDF'),
|
||||
headerRight: () => this.SendEmail()
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ContactScreen"
|
||||
component={ContactScreen}
|
||||
options={{headerTitle: () => this.LogoTitle(i18n.t('global.contacts'))}}
|
||||
initialParams={{contactAdded: false}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ContactDetailsScreen"
|
||||
component={ContactDetailsScreen}
|
||||
options={{headerTitle: () => this.LogoTitle(i18n.t('app.contact_details'))}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="SearchScreen"
|
||||
component={SearchScreen}
|
||||
options={{headerTitle: () => this.LogoTitle(i18n.t('app.results_search'))}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="MySpaceScreen"
|
||||
component={MySpaceScreen}
|
||||
options={(navigation, route) => ({
|
||||
headerTitle: () => this.LogoTitle(i18n.t('app.my_space')),
|
||||
headerRight: () => (
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
const folder = store.getState().directories.filter(tmp => tmp.id === 131);
|
||||
navigation.navigation.navigate('DirectoriesScreen', {folder: folder})
|
||||
}}
|
||||
style={{marginRight: 15}}>
|
||||
<Icon name="briefcase" size={20} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
),
|
||||
})}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
<FlashMessage position="top" />
|
||||
</NavigationContainer>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
searchInput: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#ce3029',
|
||||
width: 200,
|
||||
paddingLeft: 20,
|
||||
},
|
||||
btnSearch: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#ce3029'
|
||||
}
|
||||
});
|
||||
|
||||
export default App;
|
||||
47
src/Controllers/Helpers.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
class Helpers {
|
||||
compareValues(key, order = 'asc') {
|
||||
return function innerSort(a, b) {
|
||||
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
|
||||
// property doesn't exist on either object
|
||||
return 0;
|
||||
}
|
||||
|
||||
const varA = (typeof a[key] === 'string')
|
||||
? a[key].toUpperCase() : a[key];
|
||||
const varB = (typeof b[key] === 'string')
|
||||
? b[key].toUpperCase() : b[key];
|
||||
|
||||
let comparison = 0;
|
||||
if (varA > varB) {
|
||||
comparison = 1;
|
||||
} else if (varA < varB) {
|
||||
comparison = -1;
|
||||
}
|
||||
return (
|
||||
(order === 'desc') ? (comparison * -1) : comparison
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
isEmailValid(email) {
|
||||
const expression = /(?!.*\.{2})^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i;
|
||||
return expression.test(String(email).toLowerCase());
|
||||
}
|
||||
|
||||
generateUUID() { // Public Domain/MIT
|
||||
let d = new Date().getTime();//Timestamp
|
||||
let d2 = new Date().getUTCMilliseconds() + Math.random();//Time in microseconds since page-load or 0 if unsupported
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
let r = Math.random() * 16;//random number between 0 and 16
|
||||
if(d > 0){//Use timestamp until depleted
|
||||
r = (d + r)%16 | 0;
|
||||
d = Math.floor(d/16);
|
||||
} else {//Use microseconds since page-load if supported
|
||||
r = (d2 + r)%16 | 0;
|
||||
d2 = Math.floor(d2/16);
|
||||
}
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
}
|
||||
}
|
||||
export default new Helpers();
|
||||
270
src/Controllers/Sync.js
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
import NetInfo from '@react-native-community/netinfo';
|
||||
import Constants from 'expo-constants';
|
||||
|
||||
const axios = require('axios');
|
||||
const api_url = 'https://app.maitrecoq.fr';
|
||||
const api_token = 'Np5smPey2q94HxY8xV27c2TN7C6VpC';
|
||||
const endpoint = api_url + '/api';
|
||||
const app_uuid = '1f6eda31-1f6d-49fe-aa81-4f0aece0f7ca';
|
||||
|
||||
import UsersModel from '../Models/Users';
|
||||
import DirectoriesModel from '../Models/Directories';
|
||||
import DocumentsModel from '../Models/Documents';
|
||||
import ContactsModel from '../Models/Contacts';
|
||||
import CommonModel from '../Models/Common';
|
||||
import SendBrochureModel from '../Models/SendBrochure';
|
||||
|
||||
class Sync {
|
||||
getEndpointDocs() {
|
||||
return api_url + '/storage/docs/';
|
||||
}
|
||||
|
||||
getApiToken() {
|
||||
return api_token;
|
||||
}
|
||||
|
||||
getAppUUID() {
|
||||
return app_uuid;
|
||||
}
|
||||
|
||||
getVersion(callback, errorCallback) {
|
||||
axios({
|
||||
method: 'get',
|
||||
url: endpoint + '/version',
|
||||
responseType: 'json',
|
||||
params: {
|
||||
token: this.getApiToken(),
|
||||
app_uuid: app_uuid,
|
||||
app_version: Constants.nativeAppVersion
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
return callback(response.data.version, response.data.ShowIsActive, response.data.newApp);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
return errorCallback(error);
|
||||
});
|
||||
}
|
||||
|
||||
needSync(callback) {
|
||||
NetInfo.fetch().then(state => {
|
||||
if (state.isConnected) {
|
||||
axios({
|
||||
method: 'get',
|
||||
url: endpoint + '/version',
|
||||
responseType: 'json',
|
||||
params: {
|
||||
token: this.getApiToken(),
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
const localVersion = CommonModel.get('version');
|
||||
if (
|
||||
localVersion === undefined ||
|
||||
localVersion === false ||
|
||||
localVersion.value !== response.data.version
|
||||
) {
|
||||
return callback();
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
setInstalled() {
|
||||
CommonModel.add({key: 'installed', value: '1'});
|
||||
CommonModel.add({key: 'locale', value: 'fr'});
|
||||
CommonModel.add({key: 'ShowIsActive', value: '0'});
|
||||
}
|
||||
|
||||
getUsers(callback) {
|
||||
axios({
|
||||
method: 'get',
|
||||
url: endpoint + '/users',
|
||||
responseType: 'json',
|
||||
params: {
|
||||
token: this.getApiToken(),
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
UsersModel.clear();
|
||||
response.data.forEach(user => {
|
||||
UsersModel.add(user);
|
||||
});
|
||||
console.log('Users Update Done');
|
||||
return callback();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
getDirectories(callback) {
|
||||
axios({
|
||||
method: 'get',
|
||||
url: endpoint + '/directories',
|
||||
responseType: 'json',
|
||||
params: {
|
||||
token: this.getApiToken(),
|
||||
},
|
||||
})
|
||||
.then(function(response) {
|
||||
DirectoriesModel.clear();
|
||||
response.data.forEach(dir => {
|
||||
DirectoriesModel.add(dir);
|
||||
});
|
||||
console.log('Directories Update Done');
|
||||
return callback();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
getDocuments(callback) {
|
||||
axios({
|
||||
method: 'get',
|
||||
url: endpoint + '/documents',
|
||||
responseType: 'json',
|
||||
params: {
|
||||
token: this.getApiToken(),
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
const toDelete = DocumentsModel.clear(response.data);
|
||||
response.data.forEach(document => {
|
||||
DocumentsModel.update(document);
|
||||
});
|
||||
console.log('Documents Update Done');
|
||||
return callback(DocumentsModel.getAll(), toDelete);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
getContacts(callback, userId) {
|
||||
let contacts = ContactsModel.getAll();
|
||||
axios({
|
||||
method: 'post',
|
||||
url: endpoint + '/contacts' + '?token=' + this.getApiToken(),
|
||||
responseType: 'json',
|
||||
data: {
|
||||
contacts: contacts,
|
||||
userId: userId,
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
ContactsModel.clear();
|
||||
response.data.forEach(contact => {
|
||||
ContactsModel.add(contact);
|
||||
});
|
||||
console.log('Contacts Update Done');
|
||||
callback();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
async sendBrochure(brochures, i) {
|
||||
if (brochures.length > 0) {
|
||||
const item = brochures[i];
|
||||
let response = await axios({
|
||||
method: 'post',
|
||||
url: endpoint + '/sendBrochure',
|
||||
responseType: 'json',
|
||||
params: {
|
||||
token: this.getApiToken(),
|
||||
emails: {item},
|
||||
},
|
||||
});
|
||||
if (i < brochures.length - 1) {
|
||||
return this.sendBrochure(brochures, i + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sendBrochures(callback) {
|
||||
this.sendBrochure(SendBrochureModel.getAll(), 0).then(res => {
|
||||
SendBrochureModel.clear();
|
||||
console.log('SendBrochures Update Done');
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
updateVersion(version) {
|
||||
const localVersion = CommonModel.get('version');
|
||||
if (localVersion === false || localVersion === undefined) {
|
||||
CommonModel.add({key: 'version', value: version});
|
||||
} else {
|
||||
CommonModel.update({id: localVersion.id, key: 'version', value: version});
|
||||
}
|
||||
}
|
||||
|
||||
updateShowIsActive(value) {
|
||||
const local = CommonModel.get('ShowIsActive');
|
||||
if (local === false || local === undefined) {
|
||||
CommonModel.add({key: 'ShowIsActive', value: value});
|
||||
} else {
|
||||
CommonModel.update({id: local.id, key: 'ShowIsActive', value: value});
|
||||
}
|
||||
}
|
||||
|
||||
isLogged() {
|
||||
const logged = CommonModel.get('isLogged');
|
||||
const loggedTime = CommonModel.get('loggedTime');
|
||||
if (
|
||||
logged !== false &&
|
||||
loggedTime !== false &&
|
||||
logged !== undefined &&
|
||||
loggedTime !== undefined
|
||||
) {
|
||||
let seconds =
|
||||
Math.abs(new Date(Number(loggedTime.value)) - Date.now()) / 1000;
|
||||
if (seconds < 14400) {
|
||||
// 4h
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isInstalled() {
|
||||
const test = CommonModel.get('installed');
|
||||
if (test === false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
updateLastModificationDoc(id, date) {
|
||||
DocumentsModel.update({id: id, updated_at: date.toString()}, true);
|
||||
}
|
||||
|
||||
isSynced() {
|
||||
const SendBrochure = SendBrochureModel.getAll();
|
||||
const Contacts = ContactsModel.getAll();
|
||||
|
||||
if (SendBrochure !== undefined && Contacts !== undefined) {
|
||||
const noSynced = Contacts.filtered('synced = false');
|
||||
if (SendBrochure.length > 0 || noSynced.length > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new Sync();
|
||||
103
src/Database.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
const CommonSchema = {
|
||||
name: 'Common',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'int',
|
||||
key: 'string',
|
||||
value: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
const UserSchema = {
|
||||
name: 'Users',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'int',
|
||||
email: 'string',
|
||||
surname: 'string',
|
||||
firstname: 'string',
|
||||
password: 'string',
|
||||
role: 'string',
|
||||
authorizedFolders: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
const DirectoriesSchema = {
|
||||
name: 'Directories',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'int',
|
||||
name: 'string',
|
||||
parent: {type: 'int', optional: true},
|
||||
order: 'int',
|
||||
needAuthorization: 'int',
|
||||
},
|
||||
};
|
||||
|
||||
const DocumentsSchema = {
|
||||
name: 'Documents',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'int',
|
||||
uuid: 'string',
|
||||
title: 'string',
|
||||
type: 'string',
|
||||
path: 'string',
|
||||
directory: 'int',
|
||||
lang: 'string',
|
||||
updated_at: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
const ContactsSchema = {
|
||||
name: 'Contacts',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'int',
|
||||
email: 'string',
|
||||
type: 'string',
|
||||
other: {type: 'string', optional: true},
|
||||
select1: {type: 'string', optional: true},
|
||||
select2: {type: 'string', optional: true},
|
||||
name: 'string',
|
||||
firstname: 'string',
|
||||
company: {type: 'string', optional: true},
|
||||
address: {type: 'string', optional: true},
|
||||
phone: 'string',
|
||||
products: {type: 'string', optional: true},
|
||||
// TODO : card_url ?
|
||||
notes: {type: 'string', optional: true},
|
||||
visits: {type: 'string', optional: true},
|
||||
user_id: 'int',
|
||||
uniq_id: 'string',
|
||||
to_delete: {type: 'bool', default: false},
|
||||
synced: {type: 'bool', default: true},
|
||||
},
|
||||
};
|
||||
|
||||
const SendBrochureSchema = {
|
||||
name: 'SendBrochure',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'int',
|
||||
email: 'string',
|
||||
subject: 'string',
|
||||
message: 'string',
|
||||
documents: 'string',
|
||||
user_id: 'int'
|
||||
},
|
||||
};
|
||||
|
||||
const Database = {
|
||||
schema: [
|
||||
CommonSchema,
|
||||
UserSchema,
|
||||
DirectoriesSchema,
|
||||
DocumentsSchema,
|
||||
ContactsSchema,
|
||||
SendBrochureSchema,
|
||||
],
|
||||
encryptionKey: new Int8Array(64)
|
||||
};
|
||||
|
||||
export default Database;
|
||||
12
src/I18n.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import i18n from 'i18n-js';
|
||||
|
||||
import fr from './Locales/fr';
|
||||
import en from './Locales/en';
|
||||
import de from './Locales/de';
|
||||
|
||||
i18n.defaultLocale = 'fr';
|
||||
i18n.locale = 'fr';
|
||||
i18n.fallbacks = true;
|
||||
i18n.translations = { fr, en, de };
|
||||
|
||||
export default i18n;
|
||||
157
src/Locales/de.json
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
{
|
||||
"global": {
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"home": "Startseite",
|
||||
"search": "Suche",
|
||||
"login": "Verbindung",
|
||||
"email": "Email",
|
||||
"message_sent": "Email geschickt",
|
||||
"message_will_be_sent_after_sync": "Die Nachricht wird nach Synchronisation geschickt",
|
||||
"warning": "Achtung",
|
||||
"synchronization": "Synchronisation ",
|
||||
"videos": "Videos",
|
||||
"contacts": "Kontakt",
|
||||
"confirmation": "Bestätigung",
|
||||
"name": "Name",
|
||||
"firstname": "Vorname",
|
||||
"address": "Adresse",
|
||||
"phone": "Telefonnummer",
|
||||
"company": "Firma",
|
||||
"add": "Addieren",
|
||||
"cancel": "Abbrechen",
|
||||
"subject": "Betreff",
|
||||
"message": "Nachricht",
|
||||
"send": "Senden",
|
||||
"required": "erforderlich",
|
||||
"save": "Speichern",
|
||||
"retry": "Noch einmal versuchen",
|
||||
"next": "Nächstes"
|
||||
},
|
||||
"app": {
|
||||
"file_sharing": " Dateifreigabe",
|
||||
"folders": "Ordner",
|
||||
"video": "Video",
|
||||
"contact_details": "Kontaktdaten",
|
||||
"results_search": "Suchergebnisse",
|
||||
"my_space": "Mein Raum"
|
||||
},
|
||||
"login_s": {
|
||||
"install": "Installieren",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort"
|
||||
},
|
||||
"home_s": {
|
||||
"1": "Kataloge",
|
||||
"2": "Datenblätter",
|
||||
"3": "Andere"
|
||||
},
|
||||
"search_s": {
|
||||
"no_result": "Keine Ergebnisse"
|
||||
},
|
||||
"sync_s": {
|
||||
"download": "Download",
|
||||
"update_completed": "Update abgeschlossen",
|
||||
"updating_docs": "Dokumentaktualisierung",
|
||||
"updating_folders": "Dateien aktualisieren",
|
||||
"sending_flyers": "Brochure senden",
|
||||
"updating_contacts": "Kontakt aktualisieren",
|
||||
"user_install_completed": "Benutzerinstallation abgeschlossen",
|
||||
"updating_users": "Benutzer-Update",
|
||||
"user_install": "Benutzerinstallation",
|
||||
"start_sync": "Synchronisation starten",
|
||||
"new_app_version": "Eine neue Version der Anwendung ist verfügbar!",
|
||||
"open_store": "Öffnen Sie den App Store"
|
||||
},
|
||||
"contact_s": {
|
||||
"contact_transferred": "Kontakt weiterleiten",
|
||||
"contact_will_be_sent_after_sync": "Der Kontakt wird nach der Synchronisation gesendet",
|
||||
"confirm_contact_delete": "Möchten Sie diesen Kontakt wirklich löschen?",
|
||||
"contact_deleted": "Kontakt gelöscht",
|
||||
"contact_added": "Kontakt hinzugefügt",
|
||||
"send_flyer": "Senden Sie eine Broschüre",
|
||||
"select_contact": "Wählen Sie den Kontakt",
|
||||
"transfer": "weiterleiten",
|
||||
"contact_list": "Kontaktliste",
|
||||
"documents_list": "Liste der Dokumente"
|
||||
},
|
||||
"contact_details_s": {
|
||||
"visits": "Besuche",
|
||||
"notes": "Notes",
|
||||
"contact_saved": "Kontakt gespeichert",
|
||||
"confirm_visit_delete": "Sind Sie sicher, dass Sie diesen Besuch löschen möchten?",
|
||||
"qrcode_not_vcard_format": "Dieser QRCode ist nicht im vCard-Format",
|
||||
"customer": "Kunde",
|
||||
"prospect": "potenzieller Kunde",
|
||||
"other": "Andere",
|
||||
"description": "Beschreibung",
|
||||
"no_visits": "Kein Besuch",
|
||||
"visit_name": "Besuchsname"
|
||||
},
|
||||
"error": {
|
||||
"error": "Fehler ",
|
||||
"tablet_not_connected_internet": "Das Tablet ist nicht mit dem Internet verbunden.",
|
||||
"tablet_not_synced_last_user": "Das Tablet wurde vom vorherigen Benutzer nicht synchronisiert",
|
||||
"incorrect_credentials": "Falsche Anmeldeinformationen",
|
||||
"must_fill_login_fields": "Sie müssen die Verbindungsfelder ausfüllen",
|
||||
"invalid_email_address": "Ungültige E-Mail-Adresse",
|
||||
"error_has_occurred": "Ein Fehler ist aufgetreten",
|
||||
"must_be_connected_internet_sync": "Sie müssen mit dem Internet verbunden sein, um die Synchronisierung zu starten",
|
||||
"incomplete_form": "Unvollständiges Formular",
|
||||
"fill_all_fields_and_1_doc_min": "Bitte füllen Sie alle Felder, mindestens 1 Dokument und eine gültige E-Mail-Adresse aus.",
|
||||
"fill_other_field": "Füllen Sie das Beschreibungsfeld für: Andere",
|
||||
"specify_type_contact": "Bitte geben Sie die Art des Kontakts an",
|
||||
"invalid_name": "ungültiger Name",
|
||||
"invalid_firstname": "ungültiger Vorname",
|
||||
"camera_permission_denied": "Kameraerlaubnis verweigert",
|
||||
"pending_camera_permission": "Warten auf Kameraerlaubnis"
|
||||
},
|
||||
"custom": {
|
||||
"select-1": {
|
||||
"title": "Type de commerce",
|
||||
"c1": "Commerce de détail",
|
||||
"c2": "Home Service / Freezer Center",
|
||||
"c3": "Restauration service à table",
|
||||
"c4": "Fast food et snacking",
|
||||
"c5": "Cafétaria / Self Service",
|
||||
"c6": "Société de restauration",
|
||||
"c7": "Restauration gré à gré / cuisine Centrale",
|
||||
"c8": "Distributeur indépendant",
|
||||
"c9": "Boulangerie pâtisserie",
|
||||
"c10": "PAI",
|
||||
"c11": "Destockeur",
|
||||
"c12": "Commerce Halal",
|
||||
"c13": "Restauration d'hébergement",
|
||||
"c14": "Centrale restauration commerciale",
|
||||
"c15": "Restauration évenementielle et parc",
|
||||
"c16": "Restauration de transport",
|
||||
"c17": "Marché public",
|
||||
"c18": "Cash",
|
||||
"c19": "Distributeur national",
|
||||
"c20": "Rungis",
|
||||
"c21": "Abbatoir",
|
||||
"c22": "Autre"
|
||||
},
|
||||
"select-2": {
|
||||
"title": "Site",
|
||||
"c1": "Celvia Dinde",
|
||||
"c2": "Celvia Elaborés",
|
||||
"c3": "Celvia Sérent",
|
||||
"c4": "Procanar",
|
||||
"c5": "Celtys",
|
||||
"c6": "Farmor",
|
||||
"c7": "Robichon",
|
||||
"c8": "Les Volailles de Kerana",
|
||||
"c9": "Bosher Volaille",
|
||||
"c10": "LDC Bretagne",
|
||||
"c11": "Doux Farmor",
|
||||
"c12": "Favreau"
|
||||
},
|
||||
"interest": {
|
||||
"p1": "Poulet",
|
||||
"p2": "Dinde",
|
||||
"p3": "Canard",
|
||||
"p4": "Autre"
|
||||
}
|
||||
}
|
||||
}
|
||||
157
src/Locales/en.json
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
{
|
||||
"global": {
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"home": "Home",
|
||||
"search": "Search",
|
||||
"login": "Login",
|
||||
"email": "Email",
|
||||
"message_sent": "Message Sent",
|
||||
"message_will_be_sent_after_sync": "Message will be send after synchronization",
|
||||
"warning": "Warning",
|
||||
"synchronization": "Synchronization",
|
||||
"videos": "Videos",
|
||||
"contacts": "Contacts",
|
||||
"confirmation": "Confirmation",
|
||||
"name": "Lastname",
|
||||
"firstname": "Firstname",
|
||||
"address": "Adress",
|
||||
"phone": "Phone number",
|
||||
"company": "Company",
|
||||
"add": "Add",
|
||||
"cancel": "Cancel",
|
||||
"subject": "Object",
|
||||
"message": "Message",
|
||||
"send": "Send",
|
||||
"required": "required",
|
||||
"save": "Save",
|
||||
"retry": "Retry",
|
||||
"next": "Next"
|
||||
},
|
||||
"app": {
|
||||
"file_sharing": "File Sharing",
|
||||
"folders": "Folders",
|
||||
"video": "Video",
|
||||
"contact_details": "Contact information",
|
||||
"results_search": "Search results",
|
||||
"my_space": "Personal Dashboard"
|
||||
},
|
||||
"login_s": {
|
||||
"install": "Install",
|
||||
"username": "Username",
|
||||
"password": "Password"
|
||||
},
|
||||
"home_s": {
|
||||
"1": "Catalogs",
|
||||
"2": "Data sheets",
|
||||
"3": "Others"
|
||||
},
|
||||
"search_s": {
|
||||
"no_result": "No Result"
|
||||
},
|
||||
"sync_s": {
|
||||
"download": "Download",
|
||||
"update_completed": "Update Done",
|
||||
"updating_docs": "Updating documents",
|
||||
"updating_folders": "Updating folders",
|
||||
"sending_flyers": "Sending documents",
|
||||
"updating_contacts": "Updating contacts",
|
||||
"user_install_completed": "User install completed",
|
||||
"updating_users": "Updating Users",
|
||||
"user_install": "Installing Users",
|
||||
"start_sync": "START SYNCHRONIZATION",
|
||||
"new_app_version": "A new version of the application is available!",
|
||||
"open_store": "Open the store"
|
||||
},
|
||||
"contact_s": {
|
||||
"contact_transferred": "Contact Transferred",
|
||||
"contact_will_be_sent_after_sync": "The contact will be sent after synchronization",
|
||||
"confirm_contact_delete": "Are you sure to delete this contact ?",
|
||||
"contact_deleted": "Contact deleted",
|
||||
"contact_added": "Contact added",
|
||||
"send_flyer": "Send a document",
|
||||
"select_contact": "Select the contact",
|
||||
"transfer": "Transfer",
|
||||
"contact_list": "Contact List",
|
||||
"documents_list": "Documents List"
|
||||
},
|
||||
"contact_details_s": {
|
||||
"visits": "Visits",
|
||||
"notes": "Notes",
|
||||
"contact_saved": "Contact saved",
|
||||
"confirm_visit_delete": "are you sure to delete this visit ?",
|
||||
"qrcode_not_vcard_format": "This QRCode is not a VCard",
|
||||
"customer": "Customer",
|
||||
"prospect": "Prospect",
|
||||
"other": "Other",
|
||||
"description": "Description",
|
||||
"no_visits": "No visits available",
|
||||
"visit_name": "visits name"
|
||||
},
|
||||
"error": {
|
||||
"error": "Error",
|
||||
"tablet_not_connected_internet": "The tablet is not connected to Internet",
|
||||
"tablet_not_synced_last_user": "The previous user didn't synchronised the tablet",
|
||||
"incorrect_credentials": "Incorrect Credentials",
|
||||
"must_fill_login_fields": "You must fill the login fields.",
|
||||
"invalid_email_address": "Invalid E-mail adress",
|
||||
"error_has_occurred": "An error occured",
|
||||
"must_be_connected_internet_sync": "You must be connected to internet to be able to synchronize",
|
||||
"incomplete_form": "Incomplete Form",
|
||||
"fill_all_fields_and_1_doc_min": "Please fill up all the fields, at least one document and a valid e-mail adress",
|
||||
"fill_other_field": "You must fill the description field for : Other",
|
||||
"specify_type_contact": "You must specify the contact type",
|
||||
"invalid_name": "Invalid lastname",
|
||||
"invalid_firstname": "Invalid firstname",
|
||||
"camera_permission_denied": "Camera Permission Denied",
|
||||
"pending_camera_permission": "Waiting for the camera permission"
|
||||
},
|
||||
"custom": {
|
||||
"select-1": {
|
||||
"title": "Type de commerce",
|
||||
"c1": "Commerce de détail",
|
||||
"c2": "Home Service / Freezer Center",
|
||||
"c3": "Restauration service à table",
|
||||
"c4": "Fast food et snacking",
|
||||
"c5": "Cafétaria / Self Service",
|
||||
"c6": "Société de restauration",
|
||||
"c7": "Restauration gré à gré / cuisine Centrale",
|
||||
"c8": "Distributeur indépendant",
|
||||
"c9": "Boulangerie pâtisserie",
|
||||
"c10": "PAI",
|
||||
"c11": "Destockeur",
|
||||
"c12": "Commerce Halal",
|
||||
"c13": "Restauration d'hébergement",
|
||||
"c14": "Centrale restauration commerciale",
|
||||
"c15": "Restauration évenementielle et parc",
|
||||
"c16": "Restauration de transport",
|
||||
"c17": "Marché public",
|
||||
"c18": "Cash",
|
||||
"c19": "Distributeur national",
|
||||
"c20": "Rungis",
|
||||
"c21": "Abbatoir",
|
||||
"c22": "Autre"
|
||||
},
|
||||
"select-2": {
|
||||
"title": "Site",
|
||||
"c1": "Celvia Dinde",
|
||||
"c2": "Celvia Elaborés",
|
||||
"c3": "Celvia Sérent",
|
||||
"c4": "Procanar",
|
||||
"c5": "Celtys",
|
||||
"c6": "Farmor",
|
||||
"c7": "Robichon",
|
||||
"c8": "Les Volailles de Kerana",
|
||||
"c9": "Bosher Volaille",
|
||||
"c10": "LDC Bretagne",
|
||||
"c11": "Doux Farmor",
|
||||
"c12": "Favreau"
|
||||
},
|
||||
"interest": {
|
||||
"p1": "Poulet",
|
||||
"p2": "Dinde",
|
||||
"p3": "Canard",
|
||||
"p4": "Autre"
|
||||
}
|
||||
}
|
||||
}
|
||||
157
src/Locales/fr.json
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
{
|
||||
"global": {
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"home": "Accueil",
|
||||
"search": "Recherche",
|
||||
"login": "Connexion",
|
||||
"email": "Email",
|
||||
"message_sent": "Message envoyé",
|
||||
"message_will_be_sent_after_sync": "Le message sera envoyé après synchronisation",
|
||||
"warning": "Attention",
|
||||
"synchronization": "Synchronisation",
|
||||
"videos": "Vidéos",
|
||||
"contacts": "Contacts",
|
||||
"confirmation": "Confirmation",
|
||||
"name": "Nom",
|
||||
"firstname": "Prénom",
|
||||
"address": "Adresse",
|
||||
"phone": "Téléphone",
|
||||
"company": "Société",
|
||||
"add": "Ajouter",
|
||||
"cancel": "Annuler",
|
||||
"subject": "Objet",
|
||||
"message": "Message",
|
||||
"send": "Envoyer",
|
||||
"required": "requis",
|
||||
"save": "Enregistrer",
|
||||
"retry": "Réessayer",
|
||||
"next": "Suivant"
|
||||
},
|
||||
"app": {
|
||||
"file_sharing": "Partage de fichier",
|
||||
"folders": "Dossiers",
|
||||
"video": "Vidéo",
|
||||
"contact_details": "Détails du contact",
|
||||
"results_search": "Résultat(s) de votre recherche",
|
||||
"my_space": "Mon espace"
|
||||
},
|
||||
"login_s": {
|
||||
"install": "Installer",
|
||||
"username": "Identifiant",
|
||||
"password": "Mot de passe"
|
||||
},
|
||||
"home_s": {
|
||||
"1": "Catalogues",
|
||||
"2": "Fiches techniques",
|
||||
"3": "Autres"
|
||||
},
|
||||
"search_s": {
|
||||
"no_result": "Aucun résultat"
|
||||
},
|
||||
"sync_s": {
|
||||
"download": "Téléchargement",
|
||||
"update_completed": "Mise à jour effectuée",
|
||||
"updating_docs": "Mise à jour des documents",
|
||||
"updating_folders": "Mise à jour des dossiers",
|
||||
"sending_flyers": "Envoi des brochures",
|
||||
"updating_contacts": "Mise à jour des contacts",
|
||||
"user_install_completed": "Installation des utilisateurs effectuée",
|
||||
"updating_users": "Mise à jour des utilisateurs",
|
||||
"user_install": "Installation des utilisateurs",
|
||||
"start_sync": "LANCER LA SYNCHRONISATION",
|
||||
"new_app_version": "Une nouvelle version de l'application est disponible !",
|
||||
"open_store": "Ouvrir le store"
|
||||
},
|
||||
"contact_s": {
|
||||
"contact_transferred": "Contact transféré",
|
||||
"contact_will_be_sent_after_sync": "Le contact sera envoyé après synchronisation",
|
||||
"confirm_contact_delete": "Êtes-vous sûr de vouloir supprimer ce contact ?",
|
||||
"contact_deleted": "Contact supprimé",
|
||||
"contact_added": "Contact ajouté",
|
||||
"send_flyer": "Envoyer une brochure",
|
||||
"select_contact": "Sélectionnez le contact",
|
||||
"transfer": "Transférer",
|
||||
"contact_list": "Liste des contacts",
|
||||
"documents_list": "Liste des documents"
|
||||
},
|
||||
"contact_details_s": {
|
||||
"visits": "Visites",
|
||||
"notes": "Notes",
|
||||
"contact_saved": "Contact enregistré",
|
||||
"confirm_visit_delete": "Êtes-vous sûr de vouloir supprimer cette visite ?",
|
||||
"qrcode_not_vcard_format": "Ce QRCode n'est pas au format vCard",
|
||||
"customer": "Client",
|
||||
"prospect": "Prospect",
|
||||
"other": "Autre",
|
||||
"description": "Description",
|
||||
"no_visits": "Aucune visites",
|
||||
"visit_name": "Nom de la visite"
|
||||
},
|
||||
"error": {
|
||||
"error": "Erreur",
|
||||
"tablet_not_connected_internet": "La tablette n'est pas connectée à internet.",
|
||||
"tablet_not_synced_last_user": "La tablette n'as pas été synchronisée par l'utilisateur précédent.",
|
||||
"incorrect_credentials": "Identifiants incorrect.",
|
||||
"must_fill_login_fields": "Vous devez-remplir les champs de connexion.",
|
||||
"invalid_email_address": "Adresse email invalide",
|
||||
"error_has_occurred": "Une erreur est survenue",
|
||||
"must_be_connected_internet_sync": "Vous devez être connecté à internet pour lancer la synchronisation",
|
||||
"incomplete_form": "Formulaire incomplet",
|
||||
"fill_all_fields_and_1_doc_min": "Veuillez remplir tous les champs, 1 document minimum et une adresse email valide",
|
||||
"fill_other_field": "Vous devez remplir le champs description pour : Autre",
|
||||
"specify_type_contact": "Vous devez préciser le type de contact",
|
||||
"invalid_name": "Nom invalide",
|
||||
"invalid_firstname": "Prénom invalide",
|
||||
"camera_permission_denied": "Permission caméra refusée",
|
||||
"pending_camera_permission": "En attente de permission caméra"
|
||||
},
|
||||
"custom": {
|
||||
"select-1": {
|
||||
"title": "Type de commerce",
|
||||
"c1": "Commerce de détail",
|
||||
"c2": "Home Service / Freezer Center",
|
||||
"c3": "Restauration service à table",
|
||||
"c4": "Fast food et snacking",
|
||||
"c5": "Cafétaria / Self Service",
|
||||
"c6": "Société de restauration",
|
||||
"c7": "Restauration gré à gré / cuisine Centrale",
|
||||
"c8": "Distributeur indépendant",
|
||||
"c9": "Boulangerie pâtisserie",
|
||||
"c10": "PAI",
|
||||
"c11": "Destockeur",
|
||||
"c12": "Commerce Halal",
|
||||
"c13": "Restauration d'hébergement",
|
||||
"c14": "Centrale restauration commerciale",
|
||||
"c15": "Restauration évenementielle et parc",
|
||||
"c16": "Restauration de transport",
|
||||
"c17": "Marché public",
|
||||
"c18": "Cash",
|
||||
"c19": "Distributeur national",
|
||||
"c20": "Rungis",
|
||||
"c21": "Abbatoir",
|
||||
"c22": "Autre"
|
||||
},
|
||||
"select-2": {
|
||||
"title": "Site",
|
||||
"c1": "Celvia Dinde",
|
||||
"c2": "Celvia Elaborés",
|
||||
"c3": "Celvia Sérent",
|
||||
"c4": "Procanar",
|
||||
"c5": "Celtys",
|
||||
"c6": "Farmor",
|
||||
"c7": "Robichon",
|
||||
"c8": "Les Volailles de Kerana",
|
||||
"c9": "Bosher Volaille",
|
||||
"c10": "LDC Bretagne",
|
||||
"c11": "Doux Farmor",
|
||||
"c12": "Favreau"
|
||||
},
|
||||
"interest": {
|
||||
"p1": "Poulet",
|
||||
"p2": "Dinde",
|
||||
"p3": "Canard",
|
||||
"p4": "Autre"
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/Models/Common.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import Database from '../Database';
|
||||
|
||||
const Realm = require('realm');
|
||||
let realm = new Realm(Database);
|
||||
|
||||
class CommonModel {
|
||||
constructor() {
|
||||
this.schema = 'Common';
|
||||
}
|
||||
|
||||
add(data) {
|
||||
realm.write(() => {
|
||||
if (data.id === undefined) {
|
||||
let id: any = realm.objects(this.schema).max('id');
|
||||
data.id = id === undefined ? 1 : id + 1;
|
||||
}
|
||||
try {
|
||||
realm.create(this.schema, data, 'never');
|
||||
} catch (e) {
|
||||
console.log('db common add', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
update(data) {
|
||||
let tmp = this.get(data.key);
|
||||
if (tmp === undefined) {
|
||||
this.add(data);
|
||||
} else {
|
||||
if (data.id === undefined) {
|
||||
data.id = tmp.id;
|
||||
}
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, data, 'modified');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
let tmp = realm.objects(this.schema);
|
||||
if (tmp.length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
tmp = tmp.filtered('key = "' + key + '"')[0];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
let tmp = realm.objects(this.schema);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
const tmp = this.get(key);
|
||||
if (typeof tmp === 'object') {
|
||||
realm.write(() => {
|
||||
realm.delete(tmp);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new CommonModel();
|
||||
60
src/Models/Contacts.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import Database from '../Database';
|
||||
|
||||
const Realm = require('realm');
|
||||
let realm = new Realm(Database);
|
||||
|
||||
class ContactsModel {
|
||||
constructor() {
|
||||
this.schema = 'Contacts';
|
||||
}
|
||||
|
||||
add(contact) {
|
||||
contact.uniq_id = contact.id;
|
||||
contact.id = undefined;
|
||||
realm.write(() => {
|
||||
if (contact.id === undefined) {
|
||||
let id: any = realm.objects(this.schema).max('id');
|
||||
contact.id = id === undefined ? 1 : id + 1;
|
||||
}
|
||||
realm.create(this.schema, contact);
|
||||
});
|
||||
}
|
||||
|
||||
update(contact) {
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, contact, true);
|
||||
});
|
||||
}
|
||||
|
||||
get(id) {
|
||||
let tmp = realm.objects(this.schema).filtered('id = ' + id)[0];
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
let tmp = realm.objects(this.schema);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getByUser(userId) {
|
||||
let tmp = realm.objects(this.schema);
|
||||
tmp = tmp.filtered('to_delete = false AND user_id = ' + userId);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
delete(id) {
|
||||
const tmp = this.get(id);
|
||||
realm.write(() => {
|
||||
tmp.to_delete = true;
|
||||
// HARD DELETE - > realm.delete(tmp);
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
realm.write(() => {
|
||||
realm.delete(this.getAll());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new ContactsModel();
|
||||
104
src/Models/Directories.js
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import Database from '../Database';
|
||||
|
||||
const Realm = require('realm');
|
||||
let realm = new Realm(Database);
|
||||
|
||||
class DirectoriesModel {
|
||||
constructor() {
|
||||
this.schema = 'Directories';
|
||||
}
|
||||
|
||||
add(directory) {
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, directory);
|
||||
});
|
||||
}
|
||||
|
||||
update(directory) {
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, directory, true);
|
||||
});
|
||||
}
|
||||
|
||||
get(id) {
|
||||
let tmp = realm.objects(this.schema).filtered('id = ' + id)[0];
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getAll(role, authorizedFolders, format, lang) {
|
||||
if (authorizedFolders !== null && authorizedFolders !== undefined) {
|
||||
authorizedFolders = JSON.parse(authorizedFolders);
|
||||
}
|
||||
let tmp = realm.objects(this.schema);
|
||||
if (role === 'USER') {
|
||||
if (authorizedFolders.length > 0) {
|
||||
tmp = tmp.filtered(
|
||||
'needAuthorization = 0 OR ' +
|
||||
authorizedFolders
|
||||
.map(folder => ' id = "' + folder.id + '"')
|
||||
.join(' OR '),
|
||||
);
|
||||
} else {
|
||||
tmp = tmp.filtered(
|
||||
'needAuthorization = 0'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (format) {
|
||||
return this._recursiveDir(tmp, lang);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
clear() {
|
||||
realm.write(() => {
|
||||
realm.delete(this.getAll('SYNC', null, false));
|
||||
});
|
||||
}
|
||||
|
||||
_recursiveDir(list, lang, parent = null) {
|
||||
let root = list.filter(e => e.parent === parent);
|
||||
let tmp = [];
|
||||
for (let i = 0; i < root.length; i++) {
|
||||
let childs = this._recursiveDir(list, lang, root[i].id);
|
||||
let name = JSON.parse(root[i].name);
|
||||
tmp[i] = {};
|
||||
tmp[i].id = root[i].id;
|
||||
tmp[i].name = this.getI18nName(lang, name);
|
||||
tmp[i].items = [];
|
||||
for (let j = 0; j < childs.length; j++) {
|
||||
childs[j].value = childs[j].id;
|
||||
tmp[i].items.push(childs[j]);
|
||||
}
|
||||
if (!tmp[i].items.length) {
|
||||
delete tmp[i].items;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getI18nName(lang, name) {
|
||||
let tmp = undefined;
|
||||
switch (lang) {
|
||||
case "fr":
|
||||
tmp = name.fr;
|
||||
break;
|
||||
case "en":
|
||||
tmp = name.en;
|
||||
break;
|
||||
case "de":
|
||||
tmp = name.de;
|
||||
break;
|
||||
case "es":
|
||||
tmp = name.es;
|
||||
break;
|
||||
}
|
||||
if (tmp === undefined) {
|
||||
tmp = name.fr;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new DirectoriesModel();
|
||||
68
src/Models/Documents.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import Database from '../Database';
|
||||
|
||||
const Realm = require('realm');
|
||||
let realm = new Realm(Database);
|
||||
|
||||
class DocumentsModel {
|
||||
constructor() {
|
||||
this.schema = 'Documents';
|
||||
}
|
||||
|
||||
add(document) {
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, document, 'never');
|
||||
});
|
||||
}
|
||||
|
||||
update(document, updatedAtChange = false) {
|
||||
let tmp = this.get(document.id);
|
||||
if (tmp === undefined) {
|
||||
this.add(document);
|
||||
} else {
|
||||
realm.write(() => {
|
||||
if (updatedAtChange === false) {
|
||||
realm.create(this.schema, {
|
||||
id: tmp.id,
|
||||
uuid: document.uuid,
|
||||
title: document.title,
|
||||
type: document.type,
|
||||
path: document.path,
|
||||
directory: document.directory
|
||||
}, 'modified');
|
||||
} else {
|
||||
realm.create(this.schema, document, 'modified');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get(id) {
|
||||
let tmp = realm.objects(this.schema).filtered('id = ' + id)[0];
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getAll(authorizedDirs = false) {
|
||||
let tmp = realm.objects(this.schema);
|
||||
if (authorizedDirs) {
|
||||
authorizedDirs = authorizedDirs.map(item => item.id);
|
||||
tmp = tmp.filter(doc => authorizedDirs.includes(doc.directory));
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
clear(docs, toDelete = []) {
|
||||
const localDocs = this.getAll();
|
||||
localDocs.forEach(doc => {
|
||||
let tmp = docs.filter(item => item.uuid === doc.uuid);
|
||||
if (tmp.length === 0) {
|
||||
toDelete.push({uuid: doc.uuid, type: doc.type});
|
||||
realm.write(() => {
|
||||
realm.delete(doc);
|
||||
});
|
||||
}
|
||||
});
|
||||
return toDelete;
|
||||
}
|
||||
}
|
||||
|
||||
export default new DocumentsModel();
|
||||
49
src/Models/SendBrochure.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import Database from '../Database';
|
||||
|
||||
const Realm = require('realm');
|
||||
let realm = new Realm(Database);
|
||||
|
||||
class SendBrochureModel {
|
||||
constructor() {
|
||||
this.schema = 'SendBrochure';
|
||||
}
|
||||
|
||||
add(data) {
|
||||
realm.write(() => {
|
||||
if (data.id === undefined) {
|
||||
let id: any = realm.objects(this.schema).max('id');
|
||||
data.id = id === undefined ? 1 : id + 1;
|
||||
}
|
||||
realm.create(this.schema, data);
|
||||
});
|
||||
}
|
||||
|
||||
update(data) {
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, data, true);
|
||||
});
|
||||
}
|
||||
|
||||
get(id) {
|
||||
let tmp = realm.objects(this.schema);
|
||||
if (tmp.length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
tmp = tmp.filtered('id = ' + id)[0];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
let tmp = realm.objects(this.schema);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
clear() {
|
||||
realm.write(() => {
|
||||
realm.delete(this.getAll());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new SendBrochureModel();
|
||||
40
src/Models/Users.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import Database from '../Database';
|
||||
|
||||
const Realm = require('realm');
|
||||
let realm = new Realm(Database);
|
||||
|
||||
class UsersModel {
|
||||
constructor() {
|
||||
this.schema = 'Users';
|
||||
}
|
||||
|
||||
add(user) {
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, user);
|
||||
});
|
||||
}
|
||||
|
||||
update(user) {
|
||||
realm.write(() => {
|
||||
realm.create(this.schema, user, true);
|
||||
});
|
||||
}
|
||||
|
||||
get(id) {
|
||||
let tmp = realm.objects(this.schema).filtered('id = ' + id)[0];
|
||||
return tmp;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
let tmp = realm.objects(this.schema);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
clear() {
|
||||
realm.write(() => {
|
||||
realm.delete(this.getAll());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new UsersModel();
|
||||
606
src/Views/Contact.js
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
Button,
|
||||
FlatList,
|
||||
Text,
|
||||
Dimensions,
|
||||
TouchableOpacity,
|
||||
Alert,
|
||||
TextInput,
|
||||
Picker,
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import {connect, ReactReduxContext} from 'react-redux';
|
||||
import {showMessage} from 'react-native-flash-message';
|
||||
import {Overlay} from 'react-native-elements';
|
||||
import Helpers from "../Controllers/Helpers";
|
||||
import i18n from "../I18n";
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
users: state.users,
|
||||
contacts: state.contacts,
|
||||
documents: state.documents,
|
||||
currentUser: state.currentUser,
|
||||
};
|
||||
}
|
||||
|
||||
class ContactScreen extends React.Component {
|
||||
state = {
|
||||
contacts: [],
|
||||
sortType: 'asc',
|
||||
sortField: 'name',
|
||||
searchTxt: '',
|
||||
showTransferContact: false,
|
||||
selectedTransferUser: 0,
|
||||
contactToTransfer: {},
|
||||
showSendBrochure: false,
|
||||
selectedDocument: -1,
|
||||
sendDocumentList: [],
|
||||
selectedSendBrochureEmail: -1,
|
||||
tmpSendBrochureEmail: '',
|
||||
tmpSendBrochureSubject: '',
|
||||
tmpSendBrochureMessage: '',
|
||||
sortedDocuments: [],
|
||||
sortedEmails: [],
|
||||
};
|
||||
|
||||
sendBrochure() {
|
||||
if (
|
||||
this.state.tmpSendBrochureEmail !== '' &&
|
||||
this.state.tmpSendBrochureSubject !== '' &&
|
||||
this.state.tmpSendBrochureMessage !== '' &&
|
||||
this.state.sendDocumentList.length > 0 &&
|
||||
Helpers.isEmailValid(this.state.tmpSendBrochureEmail)
|
||||
) {
|
||||
const email = {
|
||||
email: this.state.tmpSendBrochureEmail,
|
||||
subject: this.state.tmpSendBrochureSubject,
|
||||
message: this.state.tmpSendBrochureMessage,
|
||||
documents: JSON.stringify(this.state.sendDocumentList),
|
||||
user_id: this.props.currentUser.id
|
||||
};
|
||||
this.props.dispatch({type: 'SEND_BROCHURE', data: email});
|
||||
this.setState({
|
||||
showSendBrochure: false,
|
||||
tmpSendBrochureEmail: '',
|
||||
tmpSendBrochureSubject: '',
|
||||
tmpSendBrochureMessage: '',
|
||||
selectedSendBrochureEmail: -1,
|
||||
selectedDocument: -1,
|
||||
sendDocumentList: [],
|
||||
});
|
||||
showMessage({
|
||||
message: i18n.t('global.message_sent') + '.',
|
||||
description: i18n.t('global.message_will_be_sent_after_sync') + '.',
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
} else {
|
||||
Alert.alert(
|
||||
i18n.t('error.incomplete_form'),
|
||||
i18n.t('error.fill_all_fields_and_1_doc_min') + '.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
transferContact() {
|
||||
const nextUser = this.props.users.filter(user => user.id !== this.props.currentUser.id)[this.state.selectedTransferUser];
|
||||
const transferContact = this.state.contactToTransfer;
|
||||
const contact = {
|
||||
id: transferContact.id,
|
||||
email: transferContact.email,
|
||||
type: transferContact.type,
|
||||
other: transferContact.other,
|
||||
network: transferContact.network,
|
||||
name: transferContact.name,
|
||||
firstname: transferContact.firstname,
|
||||
office: transferContact.office,
|
||||
company: transferContact.company,
|
||||
address: transferContact.address,
|
||||
phone: transferContact.phone,
|
||||
products: transferContact.products,
|
||||
notes: transferContact.note,
|
||||
visits: transferContact.visits,
|
||||
to_delete: false,
|
||||
synced: false,
|
||||
user_id: nextUser.id,
|
||||
};
|
||||
this.props.dispatch({type: 'UPDATE_CONTACT', data: contact});
|
||||
this.setState({showTransferContact: false});
|
||||
showMessage({
|
||||
message: i18n.t('contact_s.contact_transferred') + '.',
|
||||
description: i18n.t('contact_s.contact_will_be_sent_after_sync') + '.',
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
|
||||
deleteContact(id) {
|
||||
Alert.alert(
|
||||
i18n.t('global.confirmation'),
|
||||
i18n.t('contact_s.confirm_contact_delete'),
|
||||
[
|
||||
{
|
||||
text: i18n.t('global.no'),
|
||||
onPress: () => console.log('Cancel Pressed'),
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: i18n.t('global.yes'),
|
||||
onPress: () => {
|
||||
this.setState({contacts: [], sortedEmails: []});
|
||||
this.props.dispatch({type: 'DELETE_CONTACT', data: id});
|
||||
showMessage({
|
||||
message: i18n.t('contact_s.contact_deleted') + '.',
|
||||
type: 'success',
|
||||
duration: 500,
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
|
||||
Item(item) {
|
||||
return (
|
||||
<View style={styles.item}>
|
||||
<TouchableOpacity
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('ContactDetailsScreen', {
|
||||
action: 'edit',
|
||||
contactId: item.id,
|
||||
})
|
||||
}>
|
||||
<Text style={styles.clickable}>
|
||||
{item.name.toUpperCase()} {item.firstname}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.company}>{item.company}</Text>
|
||||
<Text style={styles.email}>{item.email}</Text>
|
||||
<View style={styles.buttons}>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
this.deleteContact(item.id);
|
||||
}}>
|
||||
<Icon name="trash" size={20} color={'#ce3029'}/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
this.setState({
|
||||
showTransferContact: true,
|
||||
contactToTransfer: item,
|
||||
});
|
||||
}}>
|
||||
<Icon name="paper-plane" size={20} color={'#ce3029'}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
sortContacts(field) {
|
||||
let sort;
|
||||
if (field !== this.state.sortField) {
|
||||
sort = 'asc';
|
||||
}else{
|
||||
if (this.state.sortType === 'asc') {
|
||||
sort = 'desc';
|
||||
} else {
|
||||
sort = 'asc';
|
||||
}
|
||||
}
|
||||
this.setState({contacts: this.state.contacts.sort(Helpers.compareValues(field, sort)), sortType: sort, sortField: field});
|
||||
}
|
||||
|
||||
HeaderItem() {
|
||||
return (
|
||||
<View style={styles.item}>
|
||||
<TouchableOpacity onPress={() => {
|
||||
this.sortContacts('name');
|
||||
}}>
|
||||
<Text style={styles.clickable}>{i18n.t('global.name')} <Icon name={'sort'} /></Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => {
|
||||
this.sortContacts('company');
|
||||
}}>
|
||||
<Text style={styles.clickable}>{i18n.t('global.company')} <Icon name={'sort'} /></Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.email}>{i18n.t('global.email')}</Text>
|
||||
<Text style={styles.buttons}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
handleSearchBtn() {
|
||||
if (this.state.searchTxt.length > 0) {
|
||||
let obj = this.props.contacts;
|
||||
let array = Object.keys(obj).map(function (key) { return obj[key]; });
|
||||
this.setState({contacts: array.sort(Helpers.compareValues('name', 'asc')), sortType: 'asc', searchTxt: ''});
|
||||
}
|
||||
}
|
||||
handleSearchTxt(text) {
|
||||
this.setState({searchTxt: text});
|
||||
if (text.length > 0) {
|
||||
let tmpContactsName = this.props.contacts.filter(item => item.name.toLowerCase().indexOf(text.toLowerCase()) >= 0);
|
||||
let tmpContactsCompany = this.props.contacts.filter(item => item.company.toLowerCase().indexOf(text.toLowerCase()) >= 0);
|
||||
let resSearch = Object.assign(tmpContactsName, tmpContactsCompany);
|
||||
this.setState({contacts: resSearch.sort(Helpers.compareValues('name', 'asc')), sortType: 'asc'});
|
||||
} else {
|
||||
let obj = this.props.contacts;
|
||||
let array = Object.keys(obj).map(function (key) { return obj[key]; });
|
||||
this.setState({contacts: array.sort(Helpers.compareValues('name', 'asc')), sortType: 'asc'});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (prevProps.contacts !== this.props.contacts) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
let objContacts = this.props.contacts;
|
||||
let objDocs = this.props.documents;
|
||||
let arrayContacts = Object.keys(objContacts).map(function (key) { return objContacts[key]; });
|
||||
let arrayEmails = Object.keys(objContacts).map(function (key) { return objContacts[key]; });
|
||||
let arrayDocs = Object.keys(objDocs).map(function (key) { return objDocs[key]; });
|
||||
|
||||
this.setState({
|
||||
contacts: arrayContacts.sort(Helpers.compareValues('name', 'asc')),
|
||||
sortedDocuments: arrayDocs.sort(Helpers.compareValues('title', 'asc')),
|
||||
sortedEmails: arrayEmails.sort(Helpers.compareValues('email', 'asc')),
|
||||
sortType: 'asc',
|
||||
sortField: 'name',
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {contactAdded} = this.props.route.params;
|
||||
if (contactAdded === true) {
|
||||
this.props.navigation.setParams({contactAdded: false});
|
||||
showMessage({
|
||||
message: i18n.t('contact_s.contact_added') + '.',
|
||||
type: 'success',
|
||||
duration: 500,
|
||||
});
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={{
|
||||
marginTop: 20,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Button
|
||||
color={'#ce3029'}
|
||||
title={i18n.t('global.add')}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('ContactDetailsScreen', {
|
||||
action: 'add',
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
color={'#ce3029'}
|
||||
title={i18n.t('contact_s.send_flyer')}
|
||||
onPress={() => this.setState({showSendBrochure: true})}
|
||||
/>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<TextInput placeholder={i18n.t('global.search')} style={styles.inputSearch} value={this.state.searchTxt} onChangeText={(text) => this.handleSearchTxt(text)}/>
|
||||
<TouchableOpacity style={styles.btnSearch} onPress={() => this.handleSearchBtn()}>
|
||||
{this.state.searchTxt.length > 0 ? (
|
||||
<Icon name={'times'} size={20} color={'white'}/>
|
||||
) : (
|
||||
<Icon name={'search'} size={20} color={'white'}/>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{flex: 1, marginTop: 20}}>
|
||||
<FlatList
|
||||
data={this.state.contacts}
|
||||
renderItem={({item}) => this.Item(item)}
|
||||
ListHeaderComponent={this.HeaderItem.bind(this)}
|
||||
keyExtractor={item => item.id.toString()}
|
||||
/>
|
||||
</View>
|
||||
<Overlay
|
||||
isVisible={this.state.showTransferContact}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={400}
|
||||
height={300}>
|
||||
<View
|
||||
style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text style={{marginBottom: 10}}>{i18n.t('contact_s.select_contact')} :</Text>
|
||||
<Picker
|
||||
style={{
|
||||
width: 300,
|
||||
marginBottom: 15,
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
selectedValue={this.state.selectedTransferUser}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
this.setState({selectedTransferUser: itemIndex});
|
||||
}}>
|
||||
{this.props.users.filter(user => user.id !== this.props.currentUser.id).map((item, index) => {
|
||||
return <Picker.Item label={item.email} value={index} key={item.id.toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 400,
|
||||
}}>
|
||||
<Button
|
||||
color={'#ce3029'}
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() => this.setState({showTransferContact: false})}
|
||||
/>
|
||||
<Button
|
||||
color={'#ce3029'}
|
||||
title={i18n.t('contact_s.transfer')}
|
||||
onPress={() => this.transferContact()}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
<Overlay
|
||||
isVisible={this.state.showSendBrochure}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={600}
|
||||
height={500}>
|
||||
<View style={{flex: 1}}>
|
||||
<View style={{flex: 2, flexDirection: 'row'}}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
}}>
|
||||
<TextInput
|
||||
style={styles.txtSendBrochure}
|
||||
placeholder={i18n.t('global.email')}
|
||||
autoCompleteType="email"
|
||||
autoCapitalize="none"
|
||||
keyboardType={'email-address'}
|
||||
value={this.state.tmpSendBrochureEmail}
|
||||
onChangeText={text =>
|
||||
this.setState({tmpSendBrochureEmail: text})
|
||||
}
|
||||
/>
|
||||
<Picker
|
||||
style={{
|
||||
width: 300,
|
||||
marginBottom: 15,
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
selectedValue={this.state.selectedSendBrochureEmail}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
if (itemValue !== '-1') {
|
||||
this.setState({
|
||||
tmpSendBrochureEmail: this.state.sortedEmails[itemIndex - 1]
|
||||
.email,
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<Picker.Item label={i18n.t('contact_s.contact_list') + ' :'} value="-1"/>
|
||||
{this.state.sortedEmails.map((item, index) => {
|
||||
return <Picker.Item label={item.email} value={index} key={item.id.toString()}/>;
|
||||
})}
|
||||
</Picker>
|
||||
<TextInput
|
||||
style={styles.txtSendBrochure}
|
||||
placeholder={i18n.t('global.subject')}
|
||||
value={this.state.tmpSendBrochureSubject}
|
||||
onChangeText={text =>
|
||||
this.setState({tmpSendBrochureSubject: text})
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.textAreaSendBrochure}
|
||||
placeholder={i18n.t('global.message')}
|
||||
value={this.state.tmpSendBrochureMessage}
|
||||
multiline={true}
|
||||
underlineColorAndroid="transparent"
|
||||
onChangeText={text =>
|
||||
this.setState({tmpSendBrochureMessage: text})
|
||||
}
|
||||
/>
|
||||
<Picker
|
||||
style={{
|
||||
width: 300,
|
||||
marginBottom: 15,
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
selectedValue={this.state.selectedDocument}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
if (
|
||||
itemValue !== '-1' &&
|
||||
this.state.sendDocumentList.length < 8
|
||||
) {
|
||||
let tmp = this.state.sendDocumentList;
|
||||
const toPush = this.state.sortedDocuments[itemIndex - 1];
|
||||
const test = this.state.sendDocumentList.filter(
|
||||
item => item.id === toPush.id,
|
||||
);
|
||||
if (test.length === 0) {
|
||||
tmp.push(toPush);
|
||||
this.setState({
|
||||
sendDocumentList: tmp,
|
||||
});
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<Picker.Item label={i18n.t('contact_s.documents_list') + ' :'} value="-1"/>
|
||||
{this.state.sortedDocuments.map((item, index) => {
|
||||
return (
|
||||
<Picker.Item
|
||||
label={
|
||||
item.title + ' (' + item.type.toUpperCase() + ')'
|
||||
}
|
||||
value={index}
|
||||
key={item.id.toString()}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
borderWidth: 1,
|
||||
borderColor: 'lightgrey',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 600,
|
||||
}}>
|
||||
{this.state.sendDocumentList.map((item, index) => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
let tmp = this.state.sendDocumentList;
|
||||
tmp.splice(this.props.documents[index], 1);
|
||||
this.setState({
|
||||
sendDocumentList: tmp,
|
||||
});
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
borderColor: 'lightgrey',
|
||||
padding: 5,
|
||||
margin: 5,
|
||||
borderRadius: 5,
|
||||
}}>
|
||||
<Text>
|
||||
{item.title} ({item.type.toUpperCase()}){' '}
|
||||
<Icon name="minus" size={15}/>
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 600,
|
||||
marginTop: 15,
|
||||
}}>
|
||||
<Button
|
||||
color={'#ce3029'}
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() =>
|
||||
this.setState({
|
||||
showSendBrochure: false,
|
||||
tmpSendBrochureEmail: '',
|
||||
tmpSendBrochureSubject: '',
|
||||
tmpSendBrochureMessage: '',
|
||||
selectedSendBrochureEmail: -1,
|
||||
selectedDocument: -1,
|
||||
sendDocumentList: [],
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button title={i18n.t('global.send')} onPress={() => this.sendBrochure()} color={'#ce3029'}/>
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const {width} = Dimensions.get('window');
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
},
|
||||
item: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1,
|
||||
padding: 15,
|
||||
marginHorizontal: 15,
|
||||
},
|
||||
clickable: {
|
||||
textAlign: 'center',
|
||||
fontSize: 18,
|
||||
width: width / 4,
|
||||
color: '#ce3029',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
company: {
|
||||
textAlign: 'center',
|
||||
fontSize: 15,
|
||||
width: width / 4,
|
||||
},
|
||||
email: {
|
||||
textAlign: 'center',
|
||||
fontSize: 15,
|
||||
width: width / 4,
|
||||
},
|
||||
buttons: {
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
fontSize: 15,
|
||||
width: width / 4,
|
||||
},
|
||||
button: {
|
||||
width: 35,
|
||||
height: 35,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
margin: 2,
|
||||
},
|
||||
txtSendBrochure: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'lightgrey',
|
||||
width: 280,
|
||||
marginBottom: 5,
|
||||
paddingLeft: 10,
|
||||
},
|
||||
textAreaSendBrochure: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'lightgrey',
|
||||
width: 280,
|
||||
height: 150,
|
||||
marginBottom: 5,
|
||||
paddingLeft: 10,
|
||||
textAlignVertical: 'top',
|
||||
},
|
||||
inputSearch: {
|
||||
backgroundColor: 'white',
|
||||
width: 200,
|
||||
paddingLeft: 20
|
||||
},
|
||||
btnSearch: {
|
||||
width: 50,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#ce3029'
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ContactScreen);
|
||||
877
src/Views/ContactDetails.js
Normal file
|
|
@ -0,0 +1,877 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
Button,
|
||||
TextInput,
|
||||
Picker,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
Alert,
|
||||
ScrollView
|
||||
} from 'react-native';
|
||||
import {CheckBox, ButtonGroup, Overlay} from 'react-native-elements';
|
||||
import {connect} from 'react-redux';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import {showMessage} from 'react-native-flash-message';
|
||||
import Helpers from "../Controllers/Helpers";
|
||||
import i18n from "../I18n";
|
||||
import { BarCodeScanner } from 'expo-barcode-scanner';
|
||||
import vCard from 'vcard-parser';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
contacts: state.contacts,
|
||||
currentUser: state.currentUser,
|
||||
};
|
||||
}
|
||||
|
||||
let date = new Date().getDate();
|
||||
if (date < 10) {
|
||||
date = '0' + date.toString();
|
||||
}
|
||||
let month = new Date().getMonth() + 1;
|
||||
if (month < 10) {
|
||||
month = '0' + month.toString();
|
||||
}
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
class ContactDetailsScreen extends React.Component {
|
||||
state = {
|
||||
action: 'add',
|
||||
visits: [],
|
||||
selectedVisit: 0,
|
||||
tmpVisitNote: '',
|
||||
newVisitTitle: date + '/' + month + '/' + year,
|
||||
selectedIndex: 0,
|
||||
showNewVisit: false,
|
||||
tmpID: 0,
|
||||
tmpNote: '',
|
||||
tmpName: '',
|
||||
tmpFirstname: '',
|
||||
tmpEmail: '',
|
||||
tmpCompany: '',
|
||||
tmpAddress: '',
|
||||
tmpPhone: '',
|
||||
tmpSelect1: '',
|
||||
tmpSelect2: '',
|
||||
tmpOther: '',
|
||||
clientChecked: false,
|
||||
prospectChecked: false,
|
||||
otherChecked: false,
|
||||
p1: false,
|
||||
p2: false,
|
||||
p3: false,
|
||||
p4: false,
|
||||
p5: false,
|
||||
p6: false,
|
||||
p7: false,
|
||||
p8: false,
|
||||
p9: false,
|
||||
showQRcodevCard: false,
|
||||
parsedType: null,
|
||||
parsedDatas: [],
|
||||
hasPermission: null,
|
||||
scanned: false,
|
||||
showDynContact: false,
|
||||
parsedName: '',
|
||||
parsedFirstname: '',
|
||||
parsedEmail: '',
|
||||
parsedCompany: '',
|
||||
parsedAddress: '',
|
||||
parsedPhone: ''
|
||||
};
|
||||
|
||||
updateContact() {
|
||||
let error = '';
|
||||
const {action} = this.props.route.params;
|
||||
// On récupère le texte courant
|
||||
if (this.state.visits.length > 0) {
|
||||
this.state.visits[
|
||||
this.state.selectedVisit
|
||||
].note = this.state.tmpVisitNote;
|
||||
}
|
||||
let type;
|
||||
if (this.state.clientChecked === true) {
|
||||
type = 'client';
|
||||
} else if (this.state.prospectChecked === true) {
|
||||
type = 'prospect';
|
||||
} else if (this.state.otherChecked === true) {
|
||||
type = 'other';
|
||||
if (this.state.tmpOther.length === 0) {
|
||||
error = error.concat(i18n.t('error.fill_other_field') + '.\n');
|
||||
}
|
||||
} else {
|
||||
error = error.concat(i18n.t('error.specify_type_contact') + '.\n');
|
||||
}
|
||||
if (this.state.tmpEmail.length < 5 || !Helpers.isEmailValid(this.state.tmpEmail)) {
|
||||
error = error.concat(i18n.t('error.invalid_email_address') + '.\n');
|
||||
}
|
||||
if (this.state.tmpName.length === 0) {
|
||||
error = error.concat(i18n.t('error.invalid_name') + '.\n');
|
||||
}
|
||||
if (this.state.tmpFirstname.length === 0) {
|
||||
error = error.concat(i18n.t('error.invalid_firstname') + '.\n');
|
||||
}
|
||||
|
||||
if (error.length > 0) {
|
||||
Alert.alert(i18n.t('global.warning') + ' !', error, [
|
||||
{text: 'OK'},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
} else {
|
||||
const products = [
|
||||
this.state.p1,
|
||||
this.state.p2,
|
||||
this.state.p3,
|
||||
this.state.p4,
|
||||
this.state.p5,
|
||||
this.state.p6,
|
||||
this.state.p7,
|
||||
this.state.p8,
|
||||
this.state.p9
|
||||
];
|
||||
const contact = {
|
||||
id: Helpers.generateUUID(),
|
||||
email: this.state.tmpEmail,
|
||||
type: type,
|
||||
other: this.state.tmpOther,
|
||||
select1: this.state.tmpSelect1,
|
||||
select2: this.state.tmpSelect2,
|
||||
name: this.state.tmpName,
|
||||
firstname: this.state.tmpFirstname,
|
||||
company: this.state.tmpCompany,
|
||||
address: this.state.tmpAddress,
|
||||
phone: this.state.tmpPhone,
|
||||
products: JSON.stringify(products),
|
||||
notes: this.state.tmpNote,
|
||||
visits: JSON.stringify(this.state.visits),
|
||||
to_delete: false,
|
||||
synced: false,
|
||||
};
|
||||
if (action === 'edit') {
|
||||
contact.id = this.state.tmpID;
|
||||
this.props.dispatch({type: 'UPDATE_CONTACT', data: contact});
|
||||
this.populate();
|
||||
showMessage({
|
||||
message: i18n.t('contact_details_s.contact_saved') + '.',
|
||||
type: 'success',
|
||||
duration: 500,
|
||||
});
|
||||
} else {
|
||||
contact.user_id = this.props.currentUser.id;
|
||||
this.props.dispatch({type: 'ADD_CONTACT', data: contact});
|
||||
this.props.navigation.navigate('ContactScreen', {contactAdded: true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateContactType(type) {
|
||||
if (type === 'client') {
|
||||
this.setState({
|
||||
clientChecked: true,
|
||||
prospectChecked: false,
|
||||
otherChecked: false,
|
||||
tmpOther: '',
|
||||
});
|
||||
} else if (type === 'prospect') {
|
||||
this.setState({
|
||||
clientChecked: false,
|
||||
prospectChecked: true,
|
||||
otherChecked: false,
|
||||
tmpOther: '',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
clientChecked: false,
|
||||
prospectChecked: false,
|
||||
otherChecked: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
newVisit() {
|
||||
this.state.visits.unshift({title: this.state.newVisitTitle, note: ''});
|
||||
this.state.visits[this.state.selectedVisit].note = this.state.tmpVisitNote;
|
||||
this.setState({
|
||||
tmpVisitNote: '',
|
||||
visits: this.state.visits,
|
||||
showNewVisit: false,
|
||||
selectedVisit: 0,
|
||||
});
|
||||
}
|
||||
|
||||
deleteVisit() {
|
||||
Alert.alert(
|
||||
i18n.t('global.confirmation'),
|
||||
i18n.t('contact_details_s.confirm_visit_delete'),
|
||||
[
|
||||
{
|
||||
text: i18n.t('global.no'),
|
||||
onPress: () => console.log('Cancel Pressed'),
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: i18n.t('global.yes'),
|
||||
onPress: () => {
|
||||
this.state.visits.splice(this.state.selectedVisit, 1);
|
||||
if (this.state.visits.length === 0) {
|
||||
this.setState({
|
||||
visits: this.state.visits,
|
||||
selectedVisit: 0,
|
||||
tmpVisitNote: '',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
visits: this.state.visits,
|
||||
selectedVisit: 0,
|
||||
tmpVisitNote: this.state.visits[0].note,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
|
||||
populate() {
|
||||
const {contactId} = this.props.route.params;
|
||||
const contact = this.props.contacts.filter(tmp => tmp.id === contactId)[0];
|
||||
this.setState({
|
||||
tmpID: contact.id,
|
||||
tmpNote: contact.notes,
|
||||
tmpName: contact.name,
|
||||
tmpFirstname: contact.firstname,
|
||||
tmpEmail: contact.email,
|
||||
tmpCompany: contact.company,
|
||||
tmpAddress: contact.address,
|
||||
tmpPhone: contact.phone,
|
||||
tmpSelect1: contact.select1,
|
||||
tmpSelect2: contact.select2,
|
||||
tmpOther: contact.other,
|
||||
tmpProducts: contact.product,
|
||||
});
|
||||
if (contact.type === 'client') {
|
||||
this.setState({clientChecked: true});
|
||||
} else if (contact.type === 'prospect') {
|
||||
this.setState({prospectChecked: true});
|
||||
} else {
|
||||
this.setState({otherChecked: true});
|
||||
}
|
||||
if (contact.visits !== null) {
|
||||
const tmp = JSON.parse(contact.visits);
|
||||
this.setState({visits: tmp});
|
||||
if (tmp.length > 0) {
|
||||
this.setState({tmpVisitNote: tmp[this.state.selectedVisit].note});
|
||||
}
|
||||
}
|
||||
if (contact.products !== null) {
|
||||
const tmp = JSON.parse(contact.products);
|
||||
this.setState({
|
||||
p1: tmp[0],
|
||||
p2: tmp[1],
|
||||
p3: tmp[2],
|
||||
p4: tmp[3],
|
||||
p5: tmp[4],
|
||||
p6: tmp[5],
|
||||
p7: tmp[6],
|
||||
p8: tmp[7],
|
||||
p9: tmp[8],
|
||||
p10: tmp[9],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {action} = this.props.route.params;
|
||||
if (action === 'edit') {
|
||||
this.setState({action: 'edit'});
|
||||
this.populate();
|
||||
}
|
||||
|
||||
BarCodeScanner.requestPermissionsAsync().then(res => {
|
||||
this.setState({hasPermission: res.status === 'granted'});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const handleBarCodeScanned = ({ type, data }) => {
|
||||
this.setState({scanned: true});
|
||||
if (data.includes('VCARD')) {
|
||||
let datas = vCard.parse(data);
|
||||
let tmpDatas = [];
|
||||
if (datas['n'] !== undefined) {
|
||||
const nx = Object.assign({n1: [{value: datas['n'][0].value[0]}]}, {n2: [{value: datas['n'][0].value[1]}]});
|
||||
datas = Object.assign(datas, nx);
|
||||
}
|
||||
if (datas['adr'] !== undefined) {
|
||||
let tmp = datas['adr'][0].value;
|
||||
let tmpadr = '';
|
||||
for (let i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i].length > 0) {
|
||||
if (tmpadr.length > 0 ) {
|
||||
tmpadr = tmpadr + ', ' + tmp[i];
|
||||
} else {
|
||||
tmpadr = tmp[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
datas['adr'] = [{value: tmpadr}];
|
||||
}
|
||||
Object.keys(datas).map((objectKey, index) => {
|
||||
if (objectKey === 'email' || objectKey === 'adr' || objectKey === 'tel' || objectKey === 'org' || objectKey === 'n1' || objectKey === 'n2') {
|
||||
let value = datas[objectKey][0].value;
|
||||
if (typeof value === 'string') {
|
||||
tmpDatas.push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.setState({parsedType: 'vcard', parsedDatas: tmpDatas, showQRcodevCard: false, showDynContact: true})
|
||||
} else {
|
||||
Alert.alert(i18n.t('contact_details_s.qrcode_not_vcard_format'));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView style={{flex: 1}}>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
center
|
||||
title={i18n.t('contact_details_s.customer')}
|
||||
checkedIcon="dot-circle-o"
|
||||
uncheckedIcon="circle-o"
|
||||
checked={this.state.clientChecked}
|
||||
containerStyle={styles.clearRadio}
|
||||
onPress={() => this.updateContactType('client')}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
center
|
||||
title={i18n.t('contact_details_s.prospect')}
|
||||
checkedIcon="dot-circle-o"
|
||||
uncheckedIcon="circle-o"
|
||||
checked={this.state.prospectChecked}
|
||||
containerStyle={styles.clearRadio}
|
||||
onPress={() => this.updateContactType('prospect')}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
center
|
||||
title={i18n.t('contact_details_s.other')}
|
||||
checkedIcon="dot-circle-o"
|
||||
uncheckedIcon="circle-o"
|
||||
checked={this.state.otherChecked}
|
||||
containerStyle={styles.clearRadio}
|
||||
onPress={() => this.updateContactType('other')}
|
||||
/>
|
||||
{this.state.otherChecked === true && (
|
||||
<TextInput
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
flex: 2,
|
||||
}}
|
||||
value={this.state.tmpOther}
|
||||
placeholder={i18n.t('contact_details_s.description')}
|
||||
onChangeText={text => this.setState({tmpOther: text})}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<TextInput
|
||||
style={{backgroundColor: 'white', margin: 10, padding: 10, flex: 1}}
|
||||
value={this.state.tmpName}
|
||||
placeholder={i18n.t('global.name') + ' (' + i18n.t('global.required') + ')'}
|
||||
onChangeText={text => this.setState({tmpName: text})}
|
||||
/>
|
||||
<TextInput
|
||||
style={{backgroundColor: 'white', margin: 10, padding: 10, flex: 1}}
|
||||
value={this.state.tmpFirstname}
|
||||
placeholder={i18n.t('global.firstname') + ' (' + i18n.t('global.required') + ')'}
|
||||
onChangeText={text => this.setState({tmpFirstname: text})}
|
||||
/>
|
||||
</View>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpEmail}
|
||||
placeholder={i18n.t('global.email') + ' (' + i18n.t('global.required') + ')'}
|
||||
onChangeText={text => this.setState({tmpEmail: text})}
|
||||
autoCapitalize={'none'}
|
||||
autoCorrect={false}
|
||||
keyboardType={'email-address'}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpCompany}
|
||||
placeholder={i18n.t('global.company')}
|
||||
onChangeText={text => this.setState({tmpCompany: text})}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpAddress}
|
||||
placeholder={i18n.t('global.address')}
|
||||
onChangeText={text => this.setState({tmpAddress: text})}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpPhone}
|
||||
placeholder={i18n.t('global.phone')}
|
||||
onChangeText={text => this.setState({tmpPhone: text})}
|
||||
autoCompleteType={'tel'}
|
||||
dataDetectorTypes={'phoneNumber'}
|
||||
keyboardType={'phone-pad'}
|
||||
/>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={{flex: 1, flexDirection: 'row'}}>
|
||||
<Icon name="sort-down" size={20} color={'black'} style={{zIndex: 1, position: 'absolute', right: 25, top: 20}} />
|
||||
<Picker
|
||||
selectedValue={this.state.tmpSelect1}
|
||||
style={styles.select}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
if (itemValue !== '0') {
|
||||
this.setState({tmpSelect1: itemValue});
|
||||
}
|
||||
}}>
|
||||
<Picker.Item label={i18n.t('custom.select-1.title')} value="0" />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c1')} value="commerce-de-detail" />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c2')} value={'home-service-freezer-center'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c3')} value={'restauration-service-a-table'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c4')} value={'fast-food-et-snacking'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c5')} value={'cafetaria-self-service'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c6')} value={'societe-de-restauration'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c7')} value={'restauration-gre-a-gre-cuisine-centrale'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c8')} value={'distributeur-independant'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c9')} value={'boulangerie-patisserie'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c10')} value={'pai'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c11')} value={'destockeur'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c12')} value={'commerce-halal'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c13')} value={'restauration-d-hebergement'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c14')} value={'centrale-restauration-commerciale'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c15')} value={'restauration-evenementielle-et-parc'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c16')} value={'restauration-de-transport'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c17')} value={'marche-public'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c18')} value={'cash'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c19')} value={'distributeur-national'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c20')} value={'rungis'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c21')} value={'abbatoir'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c22')} value={'autre'} />
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flex: 1, flexDirection: 'row'}}>
|
||||
<Icon name="sort-down" size={20} color={'black'} style={{zIndex: 1, position: 'absolute', right: 25, top: 20}} />
|
||||
<Picker
|
||||
selectedValue={this.state.tmpSelect2}
|
||||
style={styles.select}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
if (itemValue !== '0') {
|
||||
this.setState({tmpSelect2: itemValue});
|
||||
}
|
||||
}}>
|
||||
<Picker.Item label={i18n.t('custom.select-2.title')} value="0" />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c1')} value={'celvia-dinde'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c2')} value={'celvia-elabores'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c3')} value={'celvia-serent'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c4')} value={'procanar'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c5')} value={'celtys'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c6')} value={'farmor'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c7')} value={'robichon'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c8')} value={'les-volailles-de-keranna'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c9')} value={'bosher-volaille'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c10')} value={'ldc-bretagne'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c11')} value={'doux-farmor'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c12')} value={'favreau'} />
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p1')}
|
||||
checked={this.state.p1}
|
||||
onPress={() => this.setState({p1: !this.state.p1})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p2')}
|
||||
checked={this.state.p2}
|
||||
onPress={() => this.setState({p2: !this.state.p2})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p3')}
|
||||
checked={this.state.p3}
|
||||
onPress={() => this.setState({p3: !this.state.p3})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p4')}
|
||||
checked={this.state.p4}
|
||||
onPress={() => this.setState({p4: !this.state.p4})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
<View style={{flex: 1, margin: 5}}>
|
||||
<View style={{flex: 1}}>
|
||||
<ButtonGroup
|
||||
buttons={[i18n.t('contact_details_s.visits'), i18n.t('contact_details_s.notes')]}
|
||||
selectedButtonStyle={{backgroundColor: '#ce3029'}}
|
||||
containerStyle={{height: 30}}
|
||||
selectedIndex={this.state.selectedIndex}
|
||||
onPress={index => this.setState({selectedIndex: index})}
|
||||
/>
|
||||
{this.state.selectedIndex === 0 && (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
{this.state.visits.length > 0 && (
|
||||
<View style={{flex: 2, flexDirection: 'row'}}>
|
||||
<Icon name="sort-down" size={20} color={'black'} style={{zIndex: 1, position: 'absolute', right: 25, top: 20}} />
|
||||
<Picker
|
||||
style={styles.select}
|
||||
selectedValue={this.state.selectedVisit}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
this.state.visits[
|
||||
this.state.selectedVisit
|
||||
].note = this.state.tmpVisitNote;
|
||||
this.setState({
|
||||
visits: this.state.visits,
|
||||
selectedVisit: itemIndex,
|
||||
tmpVisitNote: this.state.visits[itemIndex].note,
|
||||
});
|
||||
}}>
|
||||
{this.state.visits.map((item, index) => {
|
||||
return <Picker.Item label={item.title} value={index} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
)}
|
||||
{this.state.visits.length === 0 && (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text>{i18n.t('contact_details_s.no_visits')}.</Text>
|
||||
</View>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-evenly',
|
||||
margin: 10,
|
||||
}}>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => this.setState({showNewVisit: true})}>
|
||||
<Icon name="plus-circle" size={20} color={'#ce3029'} />
|
||||
</TouchableOpacity>
|
||||
{this.state.visits.length > 0 && (
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => this.deleteVisit()}>
|
||||
<Icon name="trash" size={20} color={'#ce3029'} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
{this.state.visits.length > 0 && (
|
||||
<TextInput
|
||||
style={styles.textArea}
|
||||
underlineColorAndroid="transparent"
|
||||
placeholder={i18n.t('contact_details_s.notes') + '...'}
|
||||
placeholderTextColor="grey"
|
||||
multiline={true}
|
||||
value={this.state.tmpVisitNote}
|
||||
onChangeText={text => this.setState({tmpVisitNote: text})}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{this.state.selectedIndex === 1 && (
|
||||
<TextInput
|
||||
style={styles.textArea}
|
||||
underlineColorAndroid="transparent"
|
||||
placeholder={i18n.t('contact_details_s.notes') + '...'}
|
||||
placeholderTextColor="grey"
|
||||
multiline={true}
|
||||
value={this.state.tmpNote}
|
||||
onChangeText={text => this.setState({tmpNote: text})}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', justifyContent: 'space-evenly'}}>
|
||||
{this.state.action === 'add' && (
|
||||
<>
|
||||
<Button title="QRCode vCard" onPress={() => this.setState({showQRcodevCard: true})} color={'#ce3029'} />
|
||||
</>
|
||||
)}
|
||||
<Button title={i18n.t('global.save')} onPress={() => this.updateContact()} color={'#ce3029'} />
|
||||
</View>
|
||||
</View>
|
||||
<Overlay
|
||||
isVisible={this.state.showNewVisit}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={400}
|
||||
height={300}>
|
||||
<View
|
||||
style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text style={{marginBottom: 10}}>{i18n.t('contact_details_s.visit_name')} :</Text>
|
||||
<TextInput
|
||||
placeholder={i18n.t('contact_details_s.visit_name')}
|
||||
value={this.state.newVisitTitle}
|
||||
onChangeText={text => this.setState({newVisitTitle: text})}
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
borderColor: 'lightgrey',
|
||||
width: 300,
|
||||
marginBottom: 15,
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 400,
|
||||
}}>
|
||||
<Button
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() => this.setState({showNewVisit: false})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
<Button title={i18n.t('global.add')} onPress={() => this.newVisit()} color={'#ce3029'} />
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
<Overlay
|
||||
isVisible={this.state.showQRcodevCard}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={400}
|
||||
height={300}>
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
{this.state.hasPermission === true && (
|
||||
<BarCodeScanner
|
||||
onBarCodeScanned={this.state.scanned ? undefined : handleBarCodeScanned}
|
||||
barCodeTypes={[BarCodeScanner.Constants.BarCodeType.qr]}
|
||||
style={{flex: 1, width: 380, height: 200}}
|
||||
/>
|
||||
)}
|
||||
{this.state.hasPermission === false && (
|
||||
<Text>{i18n.t('error.camera_permission_denied')}.</Text>
|
||||
)}
|
||||
{this.state.hasPermission === null && (
|
||||
<Text>{i18n.t('error.pending_camera_permission')}.</Text>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 400,
|
||||
}}>
|
||||
<Button
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() => this.setState({showQRcodevCard: false, scanned: false})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
{this.state.scanned && <Button title={i18n.t('global.retry')} onPress={() => this.setState({scanned: false})} color={'#ce3029'} />}
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
<Overlay
|
||||
isVisible={this.state.showDynContact}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={400}
|
||||
height={400}>
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'space-evenly'}}>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.name')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedName}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
this.setState({parsedName: itemValue});
|
||||
}}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.firstname')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedFirstname}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedFirstname: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.email')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedEmail}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedEmail: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.company')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedCompany}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedCompany: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.address')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedAddress}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedAddress: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.phone')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedPhone}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedPhone: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 400,
|
||||
}}>
|
||||
<Button
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() => this.setState({showDynContact: false, scanned: false, parsedName: '', parsedFirstname: '', parsedEmail: '', parsedCompany: '', parsedAddress: '', parsedPhone: ''})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
<Button
|
||||
title={i18n.t('global.next')}
|
||||
onPress={() => this.setState({
|
||||
tmpName: this.state.parsedName,
|
||||
tmpFirstname: this.state.parsedFirstname,
|
||||
tmpEmail: this.state.parsedEmail,
|
||||
tmpCompany: this.state.parsedCompany,
|
||||
tmpAddress: this.state.parsedAddress,
|
||||
tmpPhone: this.state.parsedPhone,
|
||||
parsedName: '',
|
||||
parsedFirstname: '',
|
||||
parsedEmail: '',
|
||||
parsedCompany: '',
|
||||
parsedAddress: '',
|
||||
parsedPhone: '',
|
||||
showDynContact: false,
|
||||
scanned: false,
|
||||
})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
input: {
|
||||
backgroundColor: 'white',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
},
|
||||
clearCheckbox: {
|
||||
backgroundColor: '#00000000',
|
||||
borderWidth: 0,
|
||||
},
|
||||
clearRadio: {
|
||||
flex: 1,
|
||||
backgroundColor: '#00000000',
|
||||
borderWidth: 0,
|
||||
},
|
||||
select: {
|
||||
flex: 2,
|
||||
margin: 10,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
textArea: {
|
||||
flex: 1,
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
textAlignVertical: 'top',
|
||||
},
|
||||
button: {
|
||||
width: 35,
|
||||
height: 35,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
margin: 2,
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ContactDetailsScreen);
|
||||
892
src/Views/ContactDetails_RHD.js
Normal file
|
|
@ -0,0 +1,892 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
Button,
|
||||
TextInput,
|
||||
Picker,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
Alert,
|
||||
ScrollView
|
||||
} from 'react-native';
|
||||
import {CheckBox, ButtonGroup, Overlay} from 'react-native-elements';
|
||||
import {connect} from 'react-redux';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import {showMessage} from 'react-native-flash-message';
|
||||
import Helpers from "../Controllers/Helpers";
|
||||
import i18n from "../I18n";
|
||||
import { BarCodeScanner } from 'expo-barcode-scanner';
|
||||
import vCard from 'vcard-parser';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
contacts: state.contacts,
|
||||
currentUser: state.currentUser,
|
||||
};
|
||||
}
|
||||
|
||||
let date = new Date().getDate();
|
||||
if (date < 10) {
|
||||
date = '0' + date.toString();
|
||||
}
|
||||
let month = new Date().getMonth() + 1;
|
||||
if (month < 10) {
|
||||
month = '0' + month.toString();
|
||||
}
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
class ContactDetailsScreen extends React.Component {
|
||||
state = {
|
||||
action: 'add',
|
||||
visits: [],
|
||||
selectedVisit: 0,
|
||||
tmpVisitNote: '',
|
||||
newVisitTitle: date + '/' + month + '/' + year,
|
||||
selectedIndex: 0,
|
||||
showNewVisit: false,
|
||||
tmpID: 0,
|
||||
tmpNote: '',
|
||||
tmpName: '',
|
||||
tmpFirstname: '',
|
||||
tmpEmail: '',
|
||||
tmpCompany: '',
|
||||
tmpAddress: '',
|
||||
tmpPhone: '',
|
||||
tmpSelect1: '',
|
||||
tmpSelect2: '',
|
||||
tmpOther: '',
|
||||
clientChecked: false,
|
||||
prospectChecked: false,
|
||||
otherChecked: false,
|
||||
p1: false,
|
||||
p2: false,
|
||||
p3: false,
|
||||
p4: false,
|
||||
p5: false,
|
||||
p6: false,
|
||||
p7: false,
|
||||
p8: false,
|
||||
p9: false,
|
||||
showQRcodevCard: false,
|
||||
parsedType: null,
|
||||
parsedDatas: [],
|
||||
hasPermission: null,
|
||||
scanned: false,
|
||||
showDynContact: false,
|
||||
parsedName: '',
|
||||
parsedFirstname: '',
|
||||
parsedEmail: '',
|
||||
parsedCompany: '',
|
||||
parsedAddress: '',
|
||||
parsedPhone: ''
|
||||
};
|
||||
|
||||
updateContact() {
|
||||
let error = '';
|
||||
const {action} = this.props.route.params;
|
||||
// On récupère le texte courant
|
||||
if (this.state.visits.length > 0) {
|
||||
this.state.visits[
|
||||
this.state.selectedVisit
|
||||
].note = this.state.tmpVisitNote;
|
||||
}
|
||||
let type;
|
||||
if (this.state.clientChecked === true) {
|
||||
type = 'client';
|
||||
} else if (this.state.prospectChecked === true) {
|
||||
type = 'prospect';
|
||||
} else if (this.state.otherChecked === true) {
|
||||
type = 'other';
|
||||
if (this.state.tmpOther.length === 0) {
|
||||
error = error.concat(i18n.t('error.fill_other_field') + '.\n');
|
||||
}
|
||||
} else {
|
||||
error = error.concat(i18n.t('error.specify_type_contact') + '.\n');
|
||||
}
|
||||
if (this.state.tmpEmail.length < 5 || !Helpers.isEmailValid(this.state.tmpEmail)) {
|
||||
error = error.concat(i18n.t('error.invalid_email_address') + '.\n');
|
||||
}
|
||||
if (this.state.tmpName.length === 0) {
|
||||
error = error.concat(i18n.t('error.invalid_name') + '.\n');
|
||||
}
|
||||
if (this.state.tmpFirstname.length === 0) {
|
||||
error = error.concat(i18n.t('error.invalid_firstname') + '.\n');
|
||||
}
|
||||
|
||||
if (error.length > 0) {
|
||||
Alert.alert(i18n.t('global.warning') + ' !', error, [
|
||||
{text: 'OK'},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
} else {
|
||||
const products = [
|
||||
this.state.p1,
|
||||
this.state.p2,
|
||||
this.state.p3,
|
||||
this.state.p4,
|
||||
this.state.p5,
|
||||
this.state.p6,
|
||||
this.state.p7,
|
||||
this.state.p8,
|
||||
this.state.p9
|
||||
];
|
||||
const contact = {
|
||||
id: Helpers.generateUUID(),
|
||||
email: this.state.tmpEmail,
|
||||
type: type,
|
||||
other: this.state.tmpOther,
|
||||
select1: this.state.tmpSelect1,
|
||||
select2: this.state.tmpSelect2,
|
||||
name: this.state.tmpName,
|
||||
firstname: this.state.tmpFirstname,
|
||||
company: this.state.tmpCompany,
|
||||
address: this.state.tmpAddress,
|
||||
phone: this.state.tmpPhone,
|
||||
products: JSON.stringify(products),
|
||||
notes: this.state.tmpNote,
|
||||
visits: JSON.stringify(this.state.visits),
|
||||
to_delete: false,
|
||||
synced: false,
|
||||
};
|
||||
if (action === 'edit') {
|
||||
contact.id = this.state.tmpID;
|
||||
this.props.dispatch({type: 'UPDATE_CONTACT', data: contact});
|
||||
this.populate();
|
||||
showMessage({
|
||||
message: i18n.t('contact_details_s.contact_saved') + '.',
|
||||
type: 'success',
|
||||
duration: 500,
|
||||
});
|
||||
} else {
|
||||
contact.user_id = this.props.currentUser.id;
|
||||
this.props.dispatch({type: 'ADD_CONTACT', data: contact});
|
||||
this.props.navigation.navigate('ContactScreen', {contactAdded: true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateContactType(type) {
|
||||
if (type === 'client') {
|
||||
this.setState({
|
||||
clientChecked: true,
|
||||
prospectChecked: false,
|
||||
otherChecked: false,
|
||||
tmpOther: '',
|
||||
});
|
||||
} else if (type === 'prospect') {
|
||||
this.setState({
|
||||
clientChecked: false,
|
||||
prospectChecked: true,
|
||||
otherChecked: false,
|
||||
tmpOther: '',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
clientChecked: false,
|
||||
prospectChecked: false,
|
||||
otherChecked: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
newVisit() {
|
||||
this.state.visits.unshift({title: this.state.newVisitTitle, note: ''});
|
||||
this.state.visits[this.state.selectedVisit].note = this.state.tmpVisitNote;
|
||||
this.setState({
|
||||
tmpVisitNote: '',
|
||||
visits: this.state.visits,
|
||||
showNewVisit: false,
|
||||
selectedVisit: 0,
|
||||
});
|
||||
}
|
||||
|
||||
deleteVisit() {
|
||||
Alert.alert(
|
||||
i18n.t('global.confirmation'),
|
||||
i18n.t('contact_details_s.confirm_visit_delete'),
|
||||
[
|
||||
{
|
||||
text: i18n.t('global.no'),
|
||||
onPress: () => console.log('Cancel Pressed'),
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: i18n.t('global.yes'),
|
||||
onPress: () => {
|
||||
this.state.visits.splice(this.state.selectedVisit, 1);
|
||||
if (this.state.visits.length === 0) {
|
||||
this.setState({
|
||||
visits: this.state.visits,
|
||||
selectedVisit: 0,
|
||||
tmpVisitNote: '',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
visits: this.state.visits,
|
||||
selectedVisit: 0,
|
||||
tmpVisitNote: this.state.visits[0].note,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
|
||||
populate() {
|
||||
const {contactId} = this.props.route.params;
|
||||
const contact = this.props.contacts.filter(tmp => tmp.id === contactId)[0];
|
||||
this.setState({
|
||||
tmpID: contact.id,
|
||||
tmpNote: contact.notes,
|
||||
tmpName: contact.name,
|
||||
tmpFirstname: contact.firstname,
|
||||
tmpEmail: contact.email,
|
||||
tmpCompany: contact.company,
|
||||
tmpAddress: contact.address,
|
||||
tmpPhone: contact.phone,
|
||||
tmpSelect1: contact.select1,
|
||||
tmpSelect2: contact.select2,
|
||||
tmpOther: contact.other,
|
||||
tmpProducts: contact.product,
|
||||
});
|
||||
if (contact.type === 'client') {
|
||||
this.setState({clientChecked: true});
|
||||
} else if (contact.type === 'prospect') {
|
||||
this.setState({prospectChecked: true});
|
||||
} else {
|
||||
this.setState({otherChecked: true});
|
||||
}
|
||||
if (contact.visits !== null) {
|
||||
const tmp = JSON.parse(contact.visits);
|
||||
this.setState({visits: tmp});
|
||||
if (tmp.length > 0) {
|
||||
this.setState({tmpVisitNote: tmp[this.state.selectedVisit].note});
|
||||
}
|
||||
}
|
||||
if (contact.products !== null) {
|
||||
const tmp = JSON.parse(contact.products);
|
||||
this.setState({
|
||||
p1: tmp[0],
|
||||
p2: tmp[1],
|
||||
p3: tmp[2],
|
||||
p4: tmp[3],
|
||||
p5: tmp[4],
|
||||
p6: tmp[5],
|
||||
p7: tmp[6],
|
||||
p8: tmp[7],
|
||||
p9: tmp[8]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {action} = this.props.route.params;
|
||||
if (action === 'edit') {
|
||||
this.setState({action: 'edit'});
|
||||
this.populate();
|
||||
}
|
||||
|
||||
BarCodeScanner.requestPermissionsAsync().then(res => {
|
||||
this.setState({hasPermission: res.status === 'granted'});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const handleBarCodeScanned = ({ type, data }) => {
|
||||
this.setState({scanned: true});
|
||||
if (data.includes('VCARD')) {
|
||||
let datas = vCard.parse(data);
|
||||
let tmpDatas = [];
|
||||
if (datas['n'] !== undefined) {
|
||||
const nx = Object.assign({n1: [{value: datas['n'][0].value[0]}]}, {n2: [{value: datas['n'][0].value[1]}]});
|
||||
datas = Object.assign(datas, nx);
|
||||
}
|
||||
if (datas['adr'] !== undefined) {
|
||||
let tmp = datas['adr'][0].value;
|
||||
let tmpadr = '';
|
||||
for (let i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i].length > 0) {
|
||||
if (tmpadr.length > 0 ) {
|
||||
tmpadr = tmpadr + ', ' + tmp[i];
|
||||
} else {
|
||||
tmpadr = tmp[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
datas['adr'] = [{value: tmpadr}];
|
||||
}
|
||||
Object.keys(datas).map((objectKey, index) => {
|
||||
if (objectKey === 'email' || objectKey === 'adr' || objectKey === 'tel' || objectKey === 'org' || objectKey === 'n1' || objectKey === 'n2') {
|
||||
let value = datas[objectKey][0].value;
|
||||
if (typeof value === 'string') {
|
||||
tmpDatas.push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.setState({parsedType: 'vcard', parsedDatas: tmpDatas, showQRcodevCard: false, showDynContact: true})
|
||||
} else {
|
||||
Alert.alert(i18n.t('contact_details_s.qrcode_not_vcard_format'));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView style={{flex: 1}}>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
center
|
||||
title={i18n.t('contact_details_s.customer')}
|
||||
checkedIcon="dot-circle-o"
|
||||
uncheckedIcon="circle-o"
|
||||
checked={this.state.clientChecked}
|
||||
containerStyle={styles.clearRadio}
|
||||
onPress={() => this.updateContactType('client')}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
center
|
||||
title={i18n.t('contact_details_s.prospect')}
|
||||
checkedIcon="dot-circle-o"
|
||||
uncheckedIcon="circle-o"
|
||||
checked={this.state.prospectChecked}
|
||||
containerStyle={styles.clearRadio}
|
||||
onPress={() => this.updateContactType('prospect')}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
center
|
||||
title={i18n.t('contact_details_s.other')}
|
||||
checkedIcon="dot-circle-o"
|
||||
uncheckedIcon="circle-o"
|
||||
checked={this.state.otherChecked}
|
||||
containerStyle={styles.clearRadio}
|
||||
onPress={() => this.updateContactType('other')}
|
||||
/>
|
||||
{this.state.otherChecked === true && (
|
||||
<TextInput
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
flex: 2,
|
||||
}}
|
||||
value={this.state.tmpOther}
|
||||
placeholder={i18n.t('contact_details_s.description')}
|
||||
onChangeText={text => this.setState({tmpOther: text})}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<TextInput
|
||||
style={{backgroundColor: 'white', margin: 10, padding: 10, flex: 1}}
|
||||
value={this.state.tmpName}
|
||||
placeholder={i18n.t('global.name') + ' (' + i18n.t('global.required') + ')'}
|
||||
onChangeText={text => this.setState({tmpName: text})}
|
||||
/>
|
||||
<TextInput
|
||||
style={{backgroundColor: 'white', margin: 10, padding: 10, flex: 1}}
|
||||
value={this.state.tmpFirstname}
|
||||
placeholder={i18n.t('global.firstname') + ' (' + i18n.t('global.required') + ')'}
|
||||
onChangeText={text => this.setState({tmpFirstname: text})}
|
||||
/>
|
||||
</View>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpEmail}
|
||||
placeholder={i18n.t('global.email') + ' (' + i18n.t('global.required') + ')'}
|
||||
onChangeText={text => this.setState({tmpEmail: text})}
|
||||
autoCapitalize={'none'}
|
||||
autoCorrect={false}
|
||||
keyboardType={'email-address'}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpCompany}
|
||||
placeholder={i18n.t('global.company')}
|
||||
onChangeText={text => this.setState({tmpCompany: text})}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpAddress}
|
||||
placeholder={i18n.t('global.address')}
|
||||
onChangeText={text => this.setState({tmpAddress: text})}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={this.state.tmpPhone}
|
||||
placeholder={i18n.t('global.phone')}
|
||||
onChangeText={text => this.setState({tmpPhone: text})}
|
||||
autoCompleteType={'tel'}
|
||||
dataDetectorTypes={'phoneNumber'}
|
||||
keyboardType={'phone-pad'}
|
||||
/>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={{flex: 1, flexDirection: 'row'}}>
|
||||
<Icon name="sort-down" size={20} color={'black'} style={{zIndex: 1, position: 'absolute', right: 25, top: 20}} />
|
||||
<Picker
|
||||
selectedValue={this.state.tmpSelect1}
|
||||
style={styles.select}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
if (itemValue !== '0') {
|
||||
this.setState({tmpSelect1: itemValue});
|
||||
}
|
||||
}}>
|
||||
<Picker.Item label={i18n.t('custom.select-1.title')} value="0" />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c1')} value={'pole-sable'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c2')} value={'snv'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c3')} value={'pole-sud-est'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c4')} value={'aquitaine'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c5')} value={'sbv'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c6')} value={'arrive'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c7')} value={'plusieurs-filiales'} />
|
||||
<Picker.Item label={i18n.t('custom.select-1.c8')} value={'autre'} />
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flex: 1, flexDirection: 'row'}}>
|
||||
<Icon name="sort-down" size={20} color={'black'} style={{zIndex: 1, position: 'absolute', right: 25, top: 20}} />
|
||||
<Picker
|
||||
selectedValue={this.state.tmpSelect2}
|
||||
style={styles.select}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
if (itemValue !== '0') {
|
||||
this.setState({tmpSelect2: itemValue});
|
||||
}
|
||||
}}>
|
||||
<Picker.Item label={i18n.t('custom.select-2.title')} value="0" />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c1')} value={'commerce-de-detail'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c2')} value={'restauration-commerciale'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c3')} value={'restauration-collective'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c4')} value={'grossiste-distributeur-cash'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c5')} value={'pai'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c6')} value={'home-freezer-center'} />
|
||||
<Picker.Item label={i18n.t('custom.select-2.c7')} value={'autres'} />
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p1')}
|
||||
checked={this.state.p1}
|
||||
onPress={() => this.setState({p1: !this.state.p1})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p2')}
|
||||
checked={this.state.p2}
|
||||
onPress={() => this.setState({p2: !this.state.p2})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p3')}
|
||||
checked={this.state.p3}
|
||||
onPress={() => this.setState({p3: !this.state.p3})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p4')}
|
||||
checked={this.state.p4}
|
||||
onPress={() => this.setState({p4: !this.state.p4})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p5')}
|
||||
checked={this.state.p5}
|
||||
onPress={() => this.setState({p5: !this.state.p5})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p6')}
|
||||
checked={this.state.p6}
|
||||
onPress={() => this.setState({p6: !this.state.p6})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p7')}
|
||||
checked={this.state.p7}
|
||||
onPress={() => this.setState({p7: !this.state.p7})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p8')}
|
||||
checked={this.state.p8}
|
||||
onPress={() => this.setState({p8: !this.state.p8})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
<CheckBox
|
||||
checkedColor={'#ce3029'}
|
||||
title={i18n.t('custom.interest.p9')}
|
||||
checked={this.state.p9}
|
||||
onPress={() => this.setState({p9: !this.state.p9})}
|
||||
containerStyle={styles.clearCheckbox}
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
<View style={{flex: 1, margin: 5}}>
|
||||
<View style={{flex: 1}}>
|
||||
<ButtonGroup
|
||||
buttons={[i18n.t('contact_details_s.visits'), i18n.t('contact_details_s.notes')]}
|
||||
selectedButtonStyle={{backgroundColor: '#ce3029'}}
|
||||
containerStyle={{height: 30}}
|
||||
selectedIndex={this.state.selectedIndex}
|
||||
onPress={index => this.setState({selectedIndex: index})}
|
||||
/>
|
||||
{this.state.selectedIndex === 0 && (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
{this.state.visits.length > 0 && (
|
||||
<View style={{flex: 2, flexDirection: 'row'}}>
|
||||
<Icon name="sort-down" size={20} color={'black'} style={{zIndex: 1, position: 'absolute', right: 25, top: 20}} />
|
||||
<Picker
|
||||
style={styles.select}
|
||||
selectedValue={this.state.selectedVisit}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
this.state.visits[
|
||||
this.state.selectedVisit
|
||||
].note = this.state.tmpVisitNote;
|
||||
this.setState({
|
||||
visits: this.state.visits,
|
||||
selectedVisit: itemIndex,
|
||||
tmpVisitNote: this.state.visits[itemIndex].note,
|
||||
});
|
||||
}}>
|
||||
{this.state.visits.map((item, index) => {
|
||||
return <Picker.Item label={item.title} value={index} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
)}
|
||||
{this.state.visits.length === 0 && (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text>{i18n.t('contact_details_s.no_visits')}.</Text>
|
||||
</View>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-evenly',
|
||||
margin: 10,
|
||||
}}>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => this.setState({showNewVisit: true})}>
|
||||
<Icon name="plus-circle" size={20} color={'#ce3029'} />
|
||||
</TouchableOpacity>
|
||||
{this.state.visits.length > 0 && (
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => this.deleteVisit()}>
|
||||
<Icon name="trash" size={20} color={'#ce3029'} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
{this.state.visits.length > 0 && (
|
||||
<TextInput
|
||||
style={styles.textArea}
|
||||
underlineColorAndroid="transparent"
|
||||
placeholder={i18n.t('contact_details_s.notes') + '...'}
|
||||
placeholderTextColor="grey"
|
||||
multiline={true}
|
||||
value={this.state.tmpVisitNote}
|
||||
onChangeText={text => this.setState({tmpVisitNote: text})}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{this.state.selectedIndex === 1 && (
|
||||
<TextInput
|
||||
style={styles.textArea}
|
||||
underlineColorAndroid="transparent"
|
||||
placeholder={i18n.t('contact_details_s.notes') + '...'}
|
||||
placeholderTextColor="grey"
|
||||
multiline={true}
|
||||
value={this.state.tmpNote}
|
||||
onChangeText={text => this.setState({tmpNote: text})}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', justifyContent: 'space-evenly'}}>
|
||||
{this.state.action === 'add' && (
|
||||
<>
|
||||
<Button title="QRCode vCard" onPress={() => this.setState({showQRcodevCard: true})} color={'#ce3029'} />
|
||||
</>
|
||||
)}
|
||||
<Button title={i18n.t('global.save')} onPress={() => this.updateContact()} color={'#ce3029'} />
|
||||
</View>
|
||||
</View>
|
||||
<Overlay
|
||||
isVisible={this.state.showNewVisit}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={400}
|
||||
height={300}>
|
||||
<View
|
||||
style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text style={{marginBottom: 10}}>{i18n.t('contact_details_s.visit_name')} :</Text>
|
||||
<TextInput
|
||||
placeholder={i18n.t('contact_details_s.visit_name')}
|
||||
value={this.state.newVisitTitle}
|
||||
onChangeText={text => this.setState({newVisitTitle: text})}
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
borderColor: 'lightgrey',
|
||||
width: 300,
|
||||
marginBottom: 15,
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 400,
|
||||
}}>
|
||||
<Button
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() => this.setState({showNewVisit: false})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
<Button title={i18n.t('global.add')} onPress={() => this.newVisit()} color={'#ce3029'} />
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
<Overlay
|
||||
isVisible={this.state.showQRcodevCard}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={400}
|
||||
height={300}>
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
{this.state.hasPermission === true && (
|
||||
<BarCodeScanner
|
||||
onBarCodeScanned={this.state.scanned ? undefined : handleBarCodeScanned}
|
||||
barCodeTypes={[BarCodeScanner.Constants.BarCodeType.qr]}
|
||||
style={{flex: 1, width: 380, height: 200}}
|
||||
/>
|
||||
)}
|
||||
{this.state.hasPermission === false && (
|
||||
<Text>{i18n.t('error.camera_permission_denied')}.</Text>
|
||||
)}
|
||||
{this.state.hasPermission === null && (
|
||||
<Text>{i18n.t('error.pending_camera_permission')}.</Text>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 400,
|
||||
}}>
|
||||
<Button
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() => this.setState({showQRcodevCard: false, scanned: false})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
{this.state.scanned && <Button title={i18n.t('global.retry')} onPress={() => this.setState({scanned: false})} color={'#ce3029'} />}
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
<Overlay
|
||||
isVisible={this.state.showDynContact}
|
||||
windowBackgroundColor="rgba(255, 255, 255, .9)"
|
||||
overlayBackgroundColor="white"
|
||||
width={400}
|
||||
height={400}>
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'space-evenly'}}>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.name')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedName}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) => {
|
||||
this.setState({parsedName: itemValue});
|
||||
}}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.firstname')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedFirstname}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedFirstname: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.email')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedEmail}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedEmail: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.company')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedCompany}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedCompany: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.address')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedAddress}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedAddress: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<Text style={{flex: 1, paddingLeft: 10}}>{i18n.t('global.phone')}</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.parsedPhone}
|
||||
style={{height: 50, width: 250}}
|
||||
onValueChange={(itemValue, itemIndex) =>
|
||||
this.setState({parsedPhone: itemValue})
|
||||
}>
|
||||
<Picker.Item label="" value="0" key={0} />
|
||||
{this.state.parsedType === 'vcard' && this.state.parsedDatas.map((item, index) => {
|
||||
return <Picker.Item label={item} value={item} key={(index + 1).toString()} />;
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
width: 400,
|
||||
}}>
|
||||
<Button
|
||||
title={i18n.t('global.cancel')}
|
||||
onPress={() => this.setState({showDynContact: false, scanned: false, parsedName: '', parsedFirstname: '', parsedEmail: '', parsedCompany: '', parsedAddress: '', parsedPhone: ''})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
<Button
|
||||
title={i18n.t('global.next')}
|
||||
onPress={() => this.setState({
|
||||
tmpName: this.state.parsedName,
|
||||
tmpFirstname: this.state.parsedFirstname,
|
||||
tmpEmail: this.state.parsedEmail,
|
||||
tmpCompany: this.state.parsedCompany,
|
||||
tmpAddress: this.state.parsedAddress,
|
||||
tmpPhone: this.state.parsedPhone,
|
||||
parsedName: '',
|
||||
parsedFirstname: '',
|
||||
parsedEmail: '',
|
||||
parsedCompany: '',
|
||||
parsedAddress: '',
|
||||
parsedPhone: '',
|
||||
showDynContact: false,
|
||||
scanned: false,
|
||||
})}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Overlay>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
input: {
|
||||
backgroundColor: 'white',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
},
|
||||
clearCheckbox: {
|
||||
backgroundColor: '#00000000',
|
||||
borderWidth: 0,
|
||||
},
|
||||
clearRadio: {
|
||||
flex: 1,
|
||||
backgroundColor: '#00000000',
|
||||
borderWidth: 0,
|
||||
},
|
||||
select: {
|
||||
flex: 2,
|
||||
margin: 10,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
textArea: {
|
||||
flex: 1,
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
textAlignVertical: 'top',
|
||||
},
|
||||
button: {
|
||||
width: 35,
|
||||
height: 35,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
margin: 2,
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ContactDetailsScreen);
|
||||
114
src/Views/Directories.js
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import {connect} from 'react-redux';
|
||||
import TreeView from 'react-native-animated-tree-view';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
documents: state.documents,
|
||||
};
|
||||
}
|
||||
|
||||
class DirectoriesScreen extends React.Component {
|
||||
state = {
|
||||
documents: [],
|
||||
};
|
||||
|
||||
showDocuments(folder) {
|
||||
const documents = this.props.documents.filter(
|
||||
tmp => tmp.directory === folder.id,
|
||||
);
|
||||
this.setState({documents: documents});
|
||||
}
|
||||
|
||||
openDocument(document) {
|
||||
this.props.dispatch({type: 'SET_CURRENT_OPENED_DOC', data: document});
|
||||
if (document.type === 'pdf') {
|
||||
this.props.navigation.navigate('PDFScreen', {uuid: document.uuid});
|
||||
} else if (document.type === 'mp4') {
|
||||
this.props.navigation.navigate('VideoScreen', {uuid: document.uuid});
|
||||
} else {
|
||||
console.log('Inconnu');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {folder} = this.props.route.params;
|
||||
let documentList = this.state.documents.map(document => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={document.id.toString()}
|
||||
style={styles.file}
|
||||
onPress={() => this.openDocument(document)}>
|
||||
{document.type === 'pdf' ? (
|
||||
<Icon name="file-pdf" color={'white'} size={60}/>
|
||||
) : (
|
||||
<Icon name="file-video" color={'white'} size={60}/>
|
||||
)}
|
||||
<Text style={styles.fileTxt}>{document.title}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={{width: 290, borderRightWidth: 1, borderColor: 'lightgrey'}}>
|
||||
<TreeView
|
||||
data={folder}
|
||||
leftImage={{}}
|
||||
rightImage={require('../assets/img/arrow_down.png')}
|
||||
containerStyle={styles.treeviewContainer}
|
||||
onClick={tmp => this.showDocuments(tmp)}
|
||||
/>
|
||||
</View>
|
||||
<ScrollView style={styles.scrollview}>
|
||||
<View style={styles.filesContainer}>{documentList}</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
treeviewContainer: {
|
||||
width: 290,
|
||||
},
|
||||
scrollview: {
|
||||
flex: 2,
|
||||
},
|
||||
filesContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
file: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 200,
|
||||
width: 180,
|
||||
backgroundColor: '#ce3029',
|
||||
margin: 10,
|
||||
},
|
||||
fileTxt: {
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
padding: 5
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(DirectoriesScreen);
|
||||
116
src/Views/Home.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import React from 'react';
|
||||
import {View, StyleSheet, ImageBackground, TouchableOpacity, Text, Image} from 'react-native';
|
||||
import {connect} from 'react-redux';
|
||||
import NetInfo from '@react-native-community/netinfo';
|
||||
import Sync from '../Controllers/Sync';
|
||||
import SplashScreen from 'react-native-splash-screen';
|
||||
import i18n from "../I18n";
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
directories: state.directories,
|
||||
locale: state.locale,
|
||||
};
|
||||
}
|
||||
|
||||
class HomeScreen extends React.Component {
|
||||
state = {
|
||||
lastSyncCall: '2020-02-12',
|
||||
};
|
||||
componentDidMount(): void {
|
||||
this.props.dispatch({type: 'INIT'});
|
||||
SplashScreen.hide();
|
||||
}
|
||||
|
||||
unsubscribe() {
|
||||
NetInfo.addEventListener(state => {
|
||||
if (state.isConnected === true) {
|
||||
let seconds =
|
||||
Math.abs(new Date(this.state.lastSyncCall) - Date.now()) / 1000;
|
||||
//console.log(seconds);
|
||||
if (seconds > 30) {
|
||||
this.setState({lastSyncCall: Date.now()});
|
||||
Sync.needSync(this.openSync.bind(this));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openSync() {
|
||||
this.props.navigation.navigate('SyncScreen', {isInstalled: true});
|
||||
}
|
||||
|
||||
openFolder(folderID) {
|
||||
const folder = this.props.directories.filter(tmp => tmp.id === folderID);
|
||||
this.props.navigation.navigate('DirectoriesScreen', {folder: folder});
|
||||
}
|
||||
|
||||
render() {
|
||||
i18n.locale = this.props.locale;
|
||||
this.unsubscribe();
|
||||
return (
|
||||
<ImageBackground source={require('../assets/img/background.jpg')} style={{width: '100%', height: '100%'}}>
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity onPress={() => this.openFolder(2)} style={styles.button}>
|
||||
<Image source={require('../assets/img/guillemets.png')} style={styles.btnImg}/>
|
||||
<Text style={styles.btnTxt}>{i18n.t('home_s.1')}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => this.openFolder(3)} style={styles.button}>
|
||||
<Image source={require('../assets/img/guillemets.png')} style={styles.btnImg}/>
|
||||
<Text style={styles.btnTxt}>{i18n.t('home_s.2')}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => this.openFolder(4)} style={styles.button}>
|
||||
<Image source={require('../assets/img/guillemets.png')} style={styles.btnImg}/>
|
||||
<Text style={styles.btnTxt}>{i18n.t('home_s.3')}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => this.props.navigation.navigate('OnlyVideosScreen')} style={styles.button}>
|
||||
<Image source={require('../assets/img/guillemets.png')} style={styles.btnImg}/>
|
||||
<Text style={styles.btnTxt}>{i18n.t('global.videos')}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => this.props.navigation.navigate('ContactScreen')} style={styles.button}>
|
||||
<Image source={require('../assets/img/guillemets.png')} style={styles.btnImg}/>
|
||||
<Text style={styles.btnTxt}>{i18n.t('global.contacts')}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</ImageBackground>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
text: {
|
||||
height: 40,
|
||||
width: 300,
|
||||
borderColor: 'grey',
|
||||
borderWidth: 1,
|
||||
marginBottom: 20,
|
||||
},
|
||||
button: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'white',
|
||||
marginBottom: 20,
|
||||
marginRight: 50,
|
||||
width: 400,
|
||||
height: 80,
|
||||
borderRadius: 10
|
||||
},
|
||||
btnTxt: {
|
||||
fontSize: 20
|
||||
},
|
||||
btnImg: {
|
||||
position: 'absolute',
|
||||
left: 15,
|
||||
height: 50,
|
||||
width: 50,
|
||||
resizeMode: 'stretch'
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(HomeScreen);
|
||||
167
src/Views/Login.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import React from 'react';
|
||||
import {View, StyleSheet, TextInput, Button, Alert, ImageBackground, Image} from 'react-native';
|
||||
import {connect} from 'react-redux';
|
||||
import BCryptBridge from 'react-native-b-crypt-bridge';
|
||||
import Sync from '../Controllers/Sync';
|
||||
import CommonModel from '../Models/Common';
|
||||
import SplashScreen from 'react-native-splash-screen';
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import i18n from "../I18n";
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
users: state.users,
|
||||
locale: state.locale,
|
||||
};
|
||||
}
|
||||
|
||||
class LoginScreen extends React.Component {
|
||||
state = {
|
||||
login: '',
|
||||
password: '',
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
SplashScreen.hide();
|
||||
}
|
||||
|
||||
login(login, password) {
|
||||
if (login !== '' && password !== '') {
|
||||
let user = this.props.users.filter(tmp => tmp.email === login);
|
||||
if (user.length > 0) {
|
||||
user = user[0];
|
||||
BCryptBridge.verify(
|
||||
password,
|
||||
user.password,
|
||||
verified => {
|
||||
if (verified === true) {
|
||||
// Si user === last user
|
||||
const lastUser = CommonModel.get('lastUser');
|
||||
if (lastUser !== false && lastUser !== undefined && lastUser.value === user.id.toString()) {
|
||||
this.loginAction(user);
|
||||
} else {
|
||||
// Si la tablette est synchronisée avec le serveur
|
||||
if (Sync.isSynced()) {
|
||||
// Si la tablette est connectée à internet pour forcer la synchro
|
||||
NetInfo.fetch().then(state => {
|
||||
if (state.isConnected) {
|
||||
this.loginAction(user, true);
|
||||
} else {
|
||||
Alert.alert(i18n.t('error.tablet_not_connected_internet'));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Alert.alert(i18n.t('error.tablet_not_synced_last_user'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Alert.alert(i18n.t('error.incorrect_credentials'));
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
Alert.alert(i18n.t('error.incorrect_credentials'));
|
||||
}
|
||||
} else {
|
||||
Alert.alert(i18n.t('error.must_fill_login_fields'));
|
||||
}
|
||||
}
|
||||
|
||||
loginAction(user, forceSync = false) {
|
||||
this.setState({login: '', password: ''});
|
||||
CommonModel.update({
|
||||
key: 'isLogged',
|
||||
value: JSON.stringify(user),
|
||||
});
|
||||
CommonModel.update({
|
||||
key: 'loggedTime',
|
||||
value: Date.now().toString(),
|
||||
});
|
||||
CommonModel.update({key: 'lastUser', value: user.id.toString()});
|
||||
this.props.dispatch({
|
||||
type: 'SET_CURRENT_USER',
|
||||
data: JSON.stringify(user),
|
||||
});
|
||||
this.props.dispatch({type: 'SET_LOGGED', data: true});
|
||||
if (forceSync === true) {
|
||||
this.props.navigation.navigate('SyncScreen', {isInstalled: true, forceSync: true});
|
||||
}
|
||||
}
|
||||
|
||||
renderInstallBtn() {
|
||||
if (!Sync.isInstalled()) {
|
||||
return (
|
||||
<View style={{marginTop: 20}}>
|
||||
<Button
|
||||
color={'#ce3029'}
|
||||
title={i18n.t('login_s.install')}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('SyncScreen', {isInstalled: false})
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
i18n.locale = this.props.locale;
|
||||
return (
|
||||
<ImageBackground source={require('../assets/img/background.jpg')} style={{width: '100%', height: '100%'}}>
|
||||
<View style={styles.container}>
|
||||
<Image source={require('../assets/img/logo-square.png')} style={{width: 150, height: 150, marginBottom: 20}} />
|
||||
{Sync.isInstalled() && (
|
||||
<View>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
placeholder={i18n.t('login_s.username')}
|
||||
value={this.state.login}
|
||||
onChangeText={text => this.setState({login: text})}
|
||||
autoCapitalize={'none'}
|
||||
autoCorrect={false}
|
||||
keyboardType={'email-address'}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
placeholder={i18n.t('login_s.password')}
|
||||
value={this.state.password}
|
||||
onChangeText={text => this.setState({password: text})}
|
||||
secureTextEntry={true}
|
||||
autoCapitalize={'none'}
|
||||
autoCorrect={false}
|
||||
/>
|
||||
<Button
|
||||
title={i18n.t('global.login')}
|
||||
onPress={() => this.login(this.state.login, this.state.password)}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{this.renderInstallBtn()}
|
||||
</View>
|
||||
</ImageBackground>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
text: {
|
||||
height: 40,
|
||||
width: 300,
|
||||
borderColor: 'grey',
|
||||
borderWidth: 1,
|
||||
marginBottom: 20,
|
||||
backgroundColor: 'white',
|
||||
paddingLeft: 10
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(LoginScreen);
|
||||
35
src/Views/MySpace.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
documents: state.documents,
|
||||
};
|
||||
}
|
||||
|
||||
class MySpaceScreen extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Mon Espace</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(MySpaceScreen);
|
||||
95
src/Views/OnlyVideos.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import {connect} from 'react-redux';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
documents: state.documents,
|
||||
};
|
||||
}
|
||||
|
||||
class OnlyVideosScreen extends React.Component {
|
||||
state = {
|
||||
documents: [],
|
||||
};
|
||||
|
||||
showDocuments() {
|
||||
const documents = this.props.documents.filter(tmp => tmp.type === 'mp4');
|
||||
this.setState({documents: documents});
|
||||
}
|
||||
|
||||
openDocument(document) {
|
||||
this.props.dispatch({type: 'SET_CURRENT_OPENED_DOC', data: document});
|
||||
this.props.navigation.navigate('VideoScreen', {uuid: document.uuid});
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.showDocuments();
|
||||
}
|
||||
|
||||
render() {
|
||||
let documentList = this.state.documents.map(document => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={document.id.toString()}
|
||||
style={styles.file}
|
||||
onPress={() => this.openDocument(document)}>
|
||||
{document.type === 'pdf' ? (
|
||||
<Icon name="file-pdf" color={'white'} size={60}/>
|
||||
) : (
|
||||
<Icon name="file-video" color={'white'} size={60}/>
|
||||
)}
|
||||
<Text style={styles.fileTxt}>{document.title}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView style={styles.scrollview}>
|
||||
<View style={styles.filesContainer}>{documentList}</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
scrollview: {
|
||||
flex: 2,
|
||||
},
|
||||
filesContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
file: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 200,
|
||||
width: 180,
|
||||
backgroundColor: '#ce3029',
|
||||
margin: 10,
|
||||
},
|
||||
fileTxt: {
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
padding: 5
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(OnlyVideosScreen);
|
||||
57
src/Views/PDF.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import React from 'react';
|
||||
import {View, StyleSheet, Dimensions} from 'react-native';
|
||||
|
||||
import Pdf from 'react-native-pdf';
|
||||
import {FileSystem} from 'react-native-unimodules';
|
||||
|
||||
class PDFScreen extends React.Component {
|
||||
render() {
|
||||
const {uuid} = this.props.route.params;
|
||||
const source = {
|
||||
uri: FileSystem.documentDirectory + uuid + '.pdf',
|
||||
cache: false,
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Pdf
|
||||
source={source}
|
||||
onLoadComplete={(numberOfPages, filePath) => {
|
||||
console.log(`number of pages: ${numberOfPages}`);
|
||||
}}
|
||||
onPageChanged={(page, numberOfPages) => {
|
||||
console.log(`current page: ${page}`);
|
||||
}}
|
||||
onError={error => {
|
||||
console.log(error);
|
||||
}}
|
||||
onPressLink={uri => {
|
||||
console.log(`Link presse: ${uri}`);
|
||||
}}
|
||||
style={styles.pdf}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
text: {
|
||||
height: 40,
|
||||
width: 300,
|
||||
borderColor: 'grey',
|
||||
borderWidth: 1,
|
||||
marginBottom: 20,
|
||||
},
|
||||
pdf: {
|
||||
flex: 1,
|
||||
width: Dimensions.get('window').width,
|
||||
height: Dimensions.get('window').height,
|
||||
},
|
||||
});
|
||||
|
||||
export default PDFScreen;
|
||||
111
src/Views/Search.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import {connect} from 'react-redux';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import Helpers from "../Controllers/Helpers";
|
||||
import i18n from "../I18n";
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
documents: state.documents,
|
||||
};
|
||||
}
|
||||
|
||||
class SearchScreen extends React.Component {
|
||||
state = {
|
||||
documents: [],
|
||||
};
|
||||
|
||||
openDocument(document) {
|
||||
this.props.dispatch({type: 'SET_CURRENT_OPENED_DOC', data: document});
|
||||
if (document.type === 'pdf') {
|
||||
this.props.navigation.navigate('PDFScreen', {uuid: document.uuid});
|
||||
} else if (document.type === 'mp4') {
|
||||
this.props.navigation.navigate('VideoScreen', {uuid: document.uuid});
|
||||
} else {
|
||||
console.log('Inconnu');
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
const {needle, onlyVideos} = this.props.route.params;
|
||||
let objDocs = this.props.documents;
|
||||
let arrayDocs = Object.keys(objDocs).map(function (key) { return objDocs[key]; });
|
||||
let sorted = arrayDocs.sort(Helpers.compareValues('title', 'asc'));
|
||||
let filtered = sorted.filter(item => item.title.toLowerCase().indexOf(needle.toLowerCase()) >= 0);
|
||||
if (onlyVideos === true) {
|
||||
filtered = filtered.filter(item => item.type === 'mp4');
|
||||
}
|
||||
this.setState({
|
||||
documents: filtered,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let documentList = this.state.documents.map(document => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={document.id.toString()}
|
||||
style={styles.file}
|
||||
onPress={() => this.openDocument(document)}>
|
||||
{document.type === 'pdf' ? (
|
||||
<Icon name="file-pdf" color={'white'} size={60}/>
|
||||
) : (
|
||||
<Icon name="file-video" color={'white'} size={60}/>
|
||||
)}
|
||||
<Text style={styles.fileTxt}>{document.title}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView style={styles.scrollview}>
|
||||
<View style={styles.filesContainer}>{documentList}</View>
|
||||
{this.state.documents.length === 0 && (
|
||||
<Text style={{textAlign: 'center', marginTop: 50, fontSize: 30}}>{i18n.t('search_s.no_result')}</Text>
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
scrollview: {
|
||||
flex: 2,
|
||||
},
|
||||
filesContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
file: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 200,
|
||||
width: 180,
|
||||
backgroundColor: '#ce3029',
|
||||
margin: 10,
|
||||
},
|
||||
fileTxt: {
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
padding: 5
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(SearchScreen);
|
||||
320
src/Views/Sync.js
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
import React from 'react';
|
||||
import {View, StyleSheet, Button, Alert, Text, Linking} from 'react-native';
|
||||
import {connect} from 'react-redux';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
|
||||
import Sync from '../Controllers/Sync';
|
||||
|
||||
import * as Progress from 'react-native-progress';
|
||||
import NetInfo from '@react-native-community/netinfo';
|
||||
|
||||
import i18n from "../I18n";
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
documents: state.documents,
|
||||
syncVersion: state.syncVersion,
|
||||
currentUser: state.currentUser,
|
||||
};
|
||||
}
|
||||
|
||||
class SyncScreen extends React.Component {
|
||||
state = {
|
||||
progress: 0,
|
||||
txtStep: '',
|
||||
downloadProgress: 0,
|
||||
downloadingFile: false,
|
||||
processing: false,
|
||||
newApp: false
|
||||
};
|
||||
|
||||
callback(downloadProgress) {
|
||||
let progress =
|
||||
(downloadProgress.totalBytesWritten /
|
||||
downloadProgress.totalBytesExpectedToWrite) *
|
||||
100;
|
||||
progress = Math.round(progress);
|
||||
progress = progress.toString();
|
||||
this.setState({
|
||||
downloadProgress: progress,
|
||||
});
|
||||
}
|
||||
|
||||
downloadResumable(url, uuid, type) {
|
||||
return FileSystem.createDownloadResumable(
|
||||
url,
|
||||
FileSystem.documentDirectory + uuid + '.' + type,
|
||||
{},
|
||||
this.callback.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
async download(documents, progress, i) {
|
||||
if (this.state.downloadingFile === false) {
|
||||
this.setState({downloadingFile: true});
|
||||
}
|
||||
const endpoint = Sync.getEndpointDocs();
|
||||
const doc = documents[i];
|
||||
this.setState({
|
||||
txtStep:
|
||||
i18n.t('sync_s.download') + ' : ' +
|
||||
doc.title +
|
||||
' (' +
|
||||
doc.type.toUpperCase() +
|
||||
') ...',
|
||||
});
|
||||
try {
|
||||
//console.log(endpoint + doc.uuid + "." + doc.type);
|
||||
const {uri} = await this.downloadResumable(
|
||||
endpoint + doc.uuid + "." + doc.type,
|
||||
doc.uuid,
|
||||
doc.type,
|
||||
).downloadAsync();
|
||||
const {modificationTime} = await FileSystem.getInfoAsync(FileSystem.documentDirectory + doc.uuid + '.' + doc.type);
|
||||
Sync.updateLastModificationDoc(doc.id, modificationTime);
|
||||
//console.log('Finished downloading to ', uri);
|
||||
this.setState({progress: this.state.progress + progress});
|
||||
if (i < documents.length - 1) {
|
||||
return this.download(documents, progress, i + 1);
|
||||
}
|
||||
this.setState({downloadingFile: false});
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFile(docs, i) {
|
||||
if (docs.length > 0) {
|
||||
const file = docs[i];
|
||||
await FileSystem.deleteAsync(FileSystem.documentDirectory + file.uuid + '.' + file.type, {idempotent: true});
|
||||
if (i < docs.length - 1) {
|
||||
return this.deleteFile(docs, i + 1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async getToDL(docs, i, toDL = []) {
|
||||
if (docs.length < 1)
|
||||
return toDL;
|
||||
const item = docs[i];
|
||||
const {exists, modificationTime} = await FileSystem.getInfoAsync(FileSystem.documentDirectory + item.uuid + '.' + item.type);
|
||||
if (exists === false) {
|
||||
toDL.push(item);
|
||||
} else {
|
||||
if (modificationTime.toString() !== item.updated_at) {
|
||||
toDL.push(item);
|
||||
}
|
||||
}
|
||||
if (i < docs.length - 1) {
|
||||
return this.getToDL(docs, i + 1, toDL);
|
||||
}
|
||||
return toDL;
|
||||
}
|
||||
|
||||
downloadDocuments(docs, toDelete) {
|
||||
const version = this.props.syncVersion;
|
||||
this.getToDL(docs, 0).then(toDownload => {
|
||||
if (toDownload.length > 0) {
|
||||
this.setState({progress: 0.5});
|
||||
const docProgress = 0.5 / toDownload.length;
|
||||
this.download(toDownload, docProgress, 0).then(status => {
|
||||
if (status === true) {
|
||||
this.deleteFile(toDelete, 0).then(status => {
|
||||
this.finishSync(version);
|
||||
});
|
||||
} else {
|
||||
this.setState({txtStep: i18n.t('error.error_has_occurred') + '.'});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.deleteFile(toDelete, 0).then(status => {
|
||||
this.setState({progress: 1});
|
||||
this.finishSync(version);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
finishSync(version) {
|
||||
this.props.dispatch({type: 'INIT'});
|
||||
Sync.updateVersion(version);
|
||||
this.setState({txtStep: i18n.t('sync_s.update_completed') + '.', processing: false});
|
||||
if (this.state.newApp === true) {
|
||||
const appUUID = Sync.getAppUUID();
|
||||
const url = 'https://store.ldc.fr' + '/app/' + appUUID;
|
||||
Alert.alert(
|
||||
'LDC Store',
|
||||
i18n.t('sync_s.new_app_version'),
|
||||
[
|
||||
{
|
||||
text: i18n.t('global.cancel'),
|
||||
onPress: () => console.log('Cancel Pressed'),
|
||||
style: 'cancel',
|
||||
},
|
||||
{text: i18n.t('sync_s.open_store'), onPress: () => Linking.openURL(url).catch((err) => console.error('An error occurred', err))},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getDocuments() {
|
||||
this.setState({progress: 0.4, txtStep: i18n.t('sync_s.updating_docs') + '...'});
|
||||
Sync.getDocuments(this.downloadDocuments.bind(this));
|
||||
}
|
||||
|
||||
getDirectories() {
|
||||
this.setState({progress: 0.3, txtStep: i18n.t('sync_s.updating_folders') + '...'});
|
||||
Sync.getDirectories(this.getDocuments.bind(this));
|
||||
}
|
||||
|
||||
sendBrochures() {
|
||||
this.setState({progress: 0.2, txtStep: i18n.t('sync_s.sending_flyers') + '...'});
|
||||
Sync.sendBrochures(this.getDirectories.bind(this));
|
||||
}
|
||||
|
||||
getContacts() {
|
||||
this.setState({progress: 0.1, txtStep: i18n.t('sync_s.updating_contacts') + '...'});
|
||||
Sync.getContacts(this.sendBrochures.bind(this), this.props.currentUser.id);
|
||||
}
|
||||
|
||||
installProcessed() {
|
||||
Sync.setInstalled();
|
||||
this.props.dispatch({type: 'INIT_USERS'});
|
||||
this.setState({
|
||||
processing: false,
|
||||
progress: 1,
|
||||
txtStep: i18n.t('sync_s.user_install_completed') + '.',
|
||||
});
|
||||
}
|
||||
|
||||
process(version, ShowIsActive, newApp) {
|
||||
this.props.dispatch({
|
||||
type: 'UPDATE_VERSION',
|
||||
data: version,
|
||||
});
|
||||
this.props.dispatch({
|
||||
type: 'UPDATE_SHOW_IS_ACTIVE',
|
||||
data: ShowIsActive,
|
||||
});
|
||||
this.setState({newApp: newApp});
|
||||
const {isInstalled} = this.props.route.params;
|
||||
if (isInstalled === false) {
|
||||
this.setState({txtStep: i18n.t('sync_s.user_install') + '...'});
|
||||
Sync.getUsers(this.installProcessed.bind(this));
|
||||
} else {
|
||||
this.setState({txtStep: i18n.t('sync_s.updating_users') + '...'});
|
||||
Sync.getUsers(this.getContacts.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
fileProgress() {
|
||||
if (this.state.downloadingFile === true) {
|
||||
return this.state.downloadProgress + ' %';
|
||||
}
|
||||
}
|
||||
|
||||
errorCallback(error) {
|
||||
let msg = '';
|
||||
let httpCode = '';
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
console.log(error.response.data);
|
||||
console.log(error.response.status);
|
||||
console.log(error.response.headers);
|
||||
msg = i18n.t('error.error_has_occurred') + '. Err: 1';
|
||||
httpCode = error.response.status;
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
//console.log(error.request);
|
||||
msg = i18n.t('error.error_has_occurred') + '. Err: 2';
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
//console.log('Error', error.message);
|
||||
msg = i18n.t('error.error_has_occurred') + '. Err: 3';
|
||||
}
|
||||
if (httpCode.length > 0) {
|
||||
Alert.alert(
|
||||
i18n.t('error.error'),
|
||||
msg + ' (Code: ' + httpCode + ')',
|
||||
[{text: 'OK'}],
|
||||
{cancelable: false},
|
||||
);
|
||||
}else{
|
||||
Alert.alert(
|
||||
i18n.t('error.error'),
|
||||
msg,
|
||||
[{text: 'OK'}],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
this.setState({processing: false});
|
||||
}
|
||||
|
||||
checkConnection() {
|
||||
NetInfo.fetch().then(state => {
|
||||
if (state.isConnected) {
|
||||
this.setState({processing: true});
|
||||
Sync.getVersion(this.process.bind(this), this.errorCallback.bind(this));
|
||||
} else {
|
||||
Alert.alert(
|
||||
i18n.t('global.warning'),
|
||||
i18n.t('error.must_be_connected_internet_sync') + '.',
|
||||
[{text: 'OK'}],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {forceSync} = this.props.route.params;
|
||||
if (forceSync === true) {
|
||||
this.checkConnection();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const fileProgress = this.fileProgress();
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Progress.Circle
|
||||
size={250}
|
||||
progress={this.state.progress}
|
||||
showsText={true}
|
||||
color={'#ce3029'}
|
||||
/>
|
||||
<View style={{marginTop: 20}}>
|
||||
<Button
|
||||
disabled={this.state.processing}
|
||||
color={'#ce3029'}
|
||||
title={i18n.t('sync_s.start_sync')}
|
||||
onPress={() => this.checkConnection()}
|
||||
/>
|
||||
</View>
|
||||
<View style={{marginTop: 20}}>
|
||||
<Text>
|
||||
{this.state.txtStep} {fileProgress}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(SyncScreen);
|
||||
35
src/Views/Video.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react';
|
||||
import {View, StyleSheet, SafeAreaView, Dimensions} from 'react-native';
|
||||
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import {Video} from 'expo-av';
|
||||
import VideoPlayer from 'expo-video-player';
|
||||
|
||||
class VideoScreen extends React.Component {
|
||||
render() {
|
||||
const {uuid} = this.props.route.params;
|
||||
return (
|
||||
<View>
|
||||
<VideoPlayer
|
||||
height={Dimensions.get('window').height - 100}
|
||||
videoProps={{
|
||||
shouldPlay: true,
|
||||
resizeMode: Video.RESIZE_MODE_COVER,
|
||||
source: {
|
||||
uri: FileSystem.documentDirectory + uuid + '.mp4',
|
||||
},
|
||||
}}
|
||||
showFullscreenButton={false}
|
||||
sliderColor={'#ce3029'}
|
||||
showControlsOnLoad={true}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
});
|
||||
|
||||
export default VideoScreen;
|
||||
BIN
src/assets/img/arrow_down.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/img/background.jpg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/assets/img/background_intl.jpg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |