added react-native demo
3
examples/react-native/.babelrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["module:metro-react-native-babel-preset"]
|
||||
}
|
6
examples/react-native/.buckconfig
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
70
examples/react-native/.flowconfig
Normal file
@ -0,0 +1,70 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
|
||||
; Ignore polyfills
|
||||
.*/Libraries/polyfills/.*
|
||||
|
||||
; Ignore metro
|
||||
.*/node_modules/metro/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow/
|
||||
node_modules/react-native/flow-github/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
module.system=haste
|
||||
module.system.haste.use_name_reducers=true
|
||||
# get basename
|
||||
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||
# strip .js or .js.flow suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||
# strip .ios suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
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\)$' -> 'RelativeImageStub'
|
||||
|
||||
module.file_ext=.js
|
||||
module.file_ext=.jsx
|
||||
module.file_ext=.json
|
||||
module.file_ext=.native.js
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[version]
|
||||
^0.78.0
|
1
examples/react-native/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pbxproj -text
|
56
examples/react-native/.gitignore
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
# 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
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.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
|
||||
|
||||
# Bundle artifact
|
||||
*.jsbundle
|
1
examples/react-native/.watchmanconfig
Normal file
@ -0,0 +1 @@
|
||||
{}
|
0
examples/react-native/README.md
Normal file
65
examples/react-native/android/app/BUCK
Normal file
@ -0,0 +1,65 @@
|
||||
# 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
|
||||
#
|
||||
|
||||
lib_deps = []
|
||||
|
||||
for jarfile in glob(['libs/*.jar']):
|
||||
name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
|
||||
lib_deps.append(':' + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
|
||||
for aarfile in glob(['libs/*.aar']):
|
||||
name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
|
||||
lib_deps.append(':' + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
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.gundemo",
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "com.gundemo",
|
||||
res = "src/main/res",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//android/keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
package_type = "debug",
|
||||
deps = [
|
||||
":app-code",
|
||||
],
|
||||
)
|
151
examples/react-native/android/app/build.gradle
Normal file
@ -0,0 +1,151 @@
|
||||
apply plugin: "com.android.application"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
/**
|
||||
* 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
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // 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 = [
|
||||
entryFile: "index.js"
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.gundemo"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
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:
|
||||
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
|
||||
def versionCodes = ["armeabi-v7a":1, "x86":2]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-webview-bridge')
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
}
|
||||
|
||||
// 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.compile
|
||||
into 'libs'
|
||||
}
|
17
examples/react-native/android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# 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:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
@ -0,0 +1,26 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gundemo">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,15 @@
|
||||
package com.gundemo;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
|
||||
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 "GunDemo";
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.gundemo;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.Arrays;
|
||||
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() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(),
|
||||
new WebViewBridgePackage()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">GunDemo</string>
|
||||
</resources>
|
@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
39
examples/react-native/android/build.gradle
Normal file
@ -0,0 +1,39 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "27.0.3"
|
||||
minSdkVersion = 16
|
||||
compileSdkVersion = 27
|
||||
targetSdkVersion = 26
|
||||
supportLibVersion = "27.1.1"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.4'
|
||||
distributionUrl = distributionUrl.replace("bin", "all")
|
||||
}
|
18
examples/react-native/android/gradle.properties
Normal file
@ -0,0 +1,18 @@
|
||||
# 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: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# 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
|
BIN
examples/react-native/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
5
examples/react-native/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
172
examples/react-native/android/gradlew
vendored
Executable file
@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$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=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 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
|
||||
;;
|
||||
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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
84
examples/react-native/android/gradlew.bat
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
@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 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=
|
||||
|
||||
@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 init
|
||||
|
||||
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 init
|
||||
|
||||
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
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
: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 %CMD_LINE_ARGS%
|
||||
|
||||
: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
|
8
examples/react-native/android/keystores/BUCK
Normal file
@ -0,0 +1,8 @@
|
||||
keystore(
|
||||
name = "debug",
|
||||
properties = "debug.keystore.properties",
|
||||
store = "debug.keystore",
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
)
|
@ -0,0 +1,4 @@
|
||||
key.store=debug.keystore
|
||||
key.alias=androiddebugkey
|
||||
key.store.password=android
|
||||
key.alias.password=android
|
5
examples/react-native/android/settings.gradle
Normal file
@ -0,0 +1,5 @@
|
||||
rootProject.name = 'GunDemo'
|
||||
include ':react-native-webview-bridge'
|
||||
project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android')
|
||||
|
||||
include ':app'
|
4
examples/react-native/app.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "GunDemo",
|
||||
"displayName": "GunDemo"
|
||||
}
|
8
examples/react-native/index.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/** @format */
|
||||
import './shim';
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './src/App';
|
||||
import {name as appName} from './app.json';
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
console.disableYellowBox = true;
|
54
examples/react-native/ios/GunDemo-tvOS/Info.plist
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</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>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/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
24
examples/react-native/ios/GunDemo-tvOSTests/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>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</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>
|
1590
examples/react-native/ios/GunDemo.xcodeproj/project.pbxproj
Normal file
14
examples/react-native/ios/GunDemo/AppDelegate.h
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIWindow *window;
|
||||
|
||||
@end
|
35
examples/react-native/ios/GunDemo/AppDelegate.m
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSURL *jsCodeLocation;
|
||||
|
||||
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
|
||||
moduleName:@"GunDemo"
|
||||
initialProperties:nil
|
||||
launchOptions:launchOptions];
|
||||
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [UIViewController new];
|
||||
rootViewController.view = rootView;
|
||||
self.window.rootViewController = rootViewController;
|
||||
[self.window makeKeyAndVisible];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<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="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="GunDemo" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
60
examples/react-native/ios/GunDemo/Info.plist
Normal file
@ -0,0 +1,60 @@
|
||||
<?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>GunDemo</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>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/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
16
examples/react-native/ios/GunDemo/main.m
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
68
examples/react-native/ios/GunDemoTests/GunDemoTests.m
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#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 Native!"
|
||||
|
||||
@interface GunDemoTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation GunDemoTests
|
||||
|
||||
- (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;
|
||||
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||
if (level >= RCTLogLevelError) {
|
||||
redboxError = message;
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}];
|
||||
}
|
||||
|
||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||
|
||||
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
||||
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
|
||||
@end
|
24
examples/react-native/ios/GunDemoTests/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>
|
30
examples/react-native/package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "GunDemo",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": "^5.2.1",
|
||||
"encode-utf8": "^1.0.2",
|
||||
"fast-base64-encode": "^1.0.0",
|
||||
"gun": "^0.9.99999",
|
||||
"lodash": "^4.17.11",
|
||||
"react": "16.6.1",
|
||||
"react-native": "0.57.5",
|
||||
"react-native-webview-bridge": "^0.40.1",
|
||||
"serialize-error": "^3.0.0",
|
||||
"text-encoding": "^0.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "23.6.0",
|
||||
"jest": "23.6.0",
|
||||
"metro-react-native-babel-preset": "0.49.2",
|
||||
"react-test-renderer": "16.6.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native"
|
||||
}
|
||||
}
|
20
examples/react-native/shim.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
if (typeof __dirname === 'undefined') global.__dirname = '/'
|
||||
if (typeof __filename === 'undefined') global.__filename = ''
|
||||
|
||||
if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer
|
||||
|
||||
const isDev = typeof __DEV__ === 'boolean' && __DEV__
|
||||
process.env['NODE_ENV'] = isDev ? 'development' : 'production'
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.debug = isDev ? '*' : ''
|
||||
}
|
||||
|
||||
global.location = {
|
||||
protocol: 'file:',
|
||||
host: '',
|
||||
};
|
||||
|
||||
const { TextEncoder, TextDecoder } = require('text-encoding');
|
||||
|
||||
global.TextDecoder = TextDecoder;
|
||||
global.TextEncoder = TextEncoder;
|
152
examples/react-native/src/App/Demo.js
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
import * as React from 'react';
|
||||
import {View, StyleSheet, TextInput, Text, TouchableOpacity, AsyncStorage} from 'react-native';
|
||||
|
||||
import Gun from 'gun/gun';
|
||||
import 'gun/lib/open';
|
||||
import '../extensions/sea';
|
||||
|
||||
import adapter from '../extensions/asyncStorageAdapter';
|
||||
|
||||
Gun.on('create', function(db) {
|
||||
this.to.next(db);
|
||||
const pluginInterop = function(middleware) {
|
||||
return function(request) {
|
||||
this.to.next(request);
|
||||
return middleware(request, db);
|
||||
};
|
||||
}
|
||||
|
||||
// Register the adapter
|
||||
db.on('get', pluginInterop(adapter.read));
|
||||
db.on('put', pluginInterop(adapter.write));
|
||||
});
|
||||
|
||||
export class Demo extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.gun = new Gun();
|
||||
this.user = this.gun.user();
|
||||
|
||||
window.gun = this.gun;
|
||||
window.user = this.user;
|
||||
|
||||
this.state = {
|
||||
authenticated: false,
|
||||
list: [],
|
||||
listText: '',
|
||||
username: '',
|
||||
password: '',
|
||||
}
|
||||
}
|
||||
|
||||
hookUserList = () => {
|
||||
this.user.get('list').open((list) => {
|
||||
const userList = Object.keys(list).reduce((newList, key) => {
|
||||
if (!!Object.keys(list[key]).length) {
|
||||
return [...newList, {text: list[key].text, key}];
|
||||
};
|
||||
}, []);
|
||||
this.setState({
|
||||
list: userList || [],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
addToList = () => {
|
||||
this.user.get('list').set({text: this.state.listText});
|
||||
}
|
||||
|
||||
doSignin = () => {
|
||||
this.user.auth(this.state.username, this.state.password, (d) => {
|
||||
if (d.err) {
|
||||
console.log('err', d.err);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({authenticated: true});
|
||||
this.hookUserList();
|
||||
});
|
||||
}
|
||||
|
||||
doSignup = () => {
|
||||
this.user.create(this.state.username, this.state.password, () => {
|
||||
this.doSignin();
|
||||
});
|
||||
}
|
||||
|
||||
loginScreen = () => {
|
||||
return (
|
||||
<View style={styles.sub}>
|
||||
<TextInput placeholder="username" onChangeText={(username) => this.setState({username})} value={this.state.username} style={styles.input} />
|
||||
<TextInput placeholder="password" secureTextEntry={true} onChangeText={(password) => this.setState({password})} value={this.state.password} style={styles.input} />
|
||||
<TouchableOpacity onPress={this.doSignin} style={styles.button}>
|
||||
<Text>Sign in</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={this.doSignup} style={styles.button}>
|
||||
<Text>Sign up</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
userListScreen = () => {
|
||||
return (
|
||||
<View style={styles.sub}>
|
||||
{
|
||||
!!this.state.list.length && this.state.list.map((item) => <Text key={item.key}>* {item.text}</Text>)
|
||||
}
|
||||
|
||||
<TextInput placeholder="text here" onChangeText={(listText) => this.setState({listText})} value={this.state.listText} style={styles.input} />
|
||||
<TouchableOpacity onPress={this.addToList} style={styles.button}>
|
||||
<Text>Add to list</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{
|
||||
this.state.authenticated ?
|
||||
this.userListScreen() : this.loginScreen()
|
||||
}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
sub: {
|
||||
height: '50%',
|
||||
width: '60%',
|
||||
justifyContent: 'space-between',
|
||||
padding: 4,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
height: 50,
|
||||
},
|
||||
input: {
|
||||
borderColor: 'black',
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
width: '100%',
|
||||
height: 50,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: '32%',
|
||||
},
|
||||
});
|
92
examples/react-native/src/App/PolyFillCrypto.js
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
|
||||
import WebViewBridge from 'react-native-webview-bridge';
|
||||
|
||||
import { MainWorker, webViewWorkerString } from '../webview-crypto';
|
||||
|
||||
import encodeUtf8 from 'encode-utf8';
|
||||
import encodeBase64 from 'fast-base64-encode';
|
||||
|
||||
const base64EncodeString = (input) => {
|
||||
return encodeBase64(new Uint8Array(encodeUtf8(input)));
|
||||
};
|
||||
|
||||
const internalLibIOS = `
|
||||
${webViewWorkerString}
|
||||
(function () {
|
||||
var wvw = new WebViewWorker(WebViewBridge.send.bind(WebViewBridge));
|
||||
WebViewBridge.onMessage = wvw.onMainMessage.bind(wvw);
|
||||
}());
|
||||
`;
|
||||
|
||||
const intermediateLib = `
|
||||
${webViewWorkerString}
|
||||
(function () {
|
||||
var wvw = new WebViewWorker(WebViewBridge.send.bind(WebViewBridge));
|
||||
WebViewBridge.onMessage = wvw.onMainMessage.bind(wvw);
|
||||
}());
|
||||
`;
|
||||
|
||||
const internalLibAndroid = `eval(window.atob('${base64EncodeString(intermediateLib)}'))`;
|
||||
|
||||
export default class PolyfillCrypto extends React.Component {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
let worker;
|
||||
const uri = 'file:///android_asset/html/blank.html';
|
||||
return (
|
||||
<View style={styles.hidden}>
|
||||
<WebViewBridge
|
||||
ref={(c) => {
|
||||
if (c && !worker) {
|
||||
worker = new MainWorker(c.sendToBridge, this.props.debug);
|
||||
|
||||
if (window.crypto) {
|
||||
// we are in chrome debugger
|
||||
// this means overridng the crypto object itself won't
|
||||
// work, so we have to override all of it's methods
|
||||
for (const name in worker.crypto.subtle) {
|
||||
window.crypto.subtle[name] = worker.crypto.subtle[name];
|
||||
}
|
||||
window.crypto.fake = true;
|
||||
} else {
|
||||
window.crypto = worker.crypto;
|
||||
}
|
||||
window.crypto.loaded = true;
|
||||
console.log('*** poly injected', window.crypto);
|
||||
}
|
||||
}}
|
||||
onBridgeMessage={
|
||||
// can't refer to this.state.onBridgeMessage directly
|
||||
// because it is not defined when this component is first
|
||||
// started, only set in `ref`
|
||||
(message) => {
|
||||
worker.onWebViewMessage(message);
|
||||
}
|
||||
}
|
||||
injectedJavaScript={
|
||||
Platform.OS === 'android' ? internalLibAndroid : internalLibIOS
|
||||
}
|
||||
onError={(error) => {
|
||||
console.warn('Error creating webview: ', error);
|
||||
}}
|
||||
javaScriptEnabled={true}
|
||||
source={{
|
||||
uri: Platform.OS === 'android' ? uri : 'about:blank',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
hidden: {
|
||||
height: 0,
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
26
examples/react-native/src/App/app.js
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import {Demo} from './Demo';
|
||||
|
||||
import PolyFillCrypto from './PolyFillCrypto';
|
||||
|
||||
|
||||
export class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<PolyFillCrypto />
|
||||
<Demo/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
1
examples/react-native/src/App/index.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {App as default} from './app';
|
75
examples/react-native/src/extensions/asyncStorageAdapter.js
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
import * as Gun from 'gun';
|
||||
import { AsyncStorage } from 'react-native';
|
||||
|
||||
const readNode = (key, cb) => {
|
||||
AsyncStorage.getItem(key || '', cb);
|
||||
};
|
||||
|
||||
const read = (request, db) => {
|
||||
const { get } = request;
|
||||
|
||||
const dedupid = request['#'];
|
||||
const key = get['#'];
|
||||
const field = get['.'];
|
||||
|
||||
const done = (err, data) => {
|
||||
if (!data && !err) {
|
||||
db.on('in', {
|
||||
'@': dedupid,
|
||||
put: null,
|
||||
err: null,
|
||||
});
|
||||
} else {
|
||||
db.on('in', {
|
||||
'@': dedupid,
|
||||
put: Gun.graph.node(data),
|
||||
err,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const acknowledgeRet = (err, result) => {
|
||||
if (err) {
|
||||
done(err);
|
||||
} else if (result === null) {
|
||||
// Nothing found
|
||||
done(null);
|
||||
} else {
|
||||
const temp = JSON.parse(result);
|
||||
if (field) {
|
||||
done(null, temp[field] || null);
|
||||
} else {
|
||||
done(null, temp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
readNode(key || '', acknowledgeRet);
|
||||
};
|
||||
|
||||
const write = (request, db) => {
|
||||
const { put: graph } = request;
|
||||
const keys = Object.keys(graph);
|
||||
const dedupid = graph['#'];
|
||||
|
||||
const instructions = keys.map((key) => {
|
||||
return [key, JSON.stringify(graph[key] || {})];
|
||||
});
|
||||
|
||||
AsyncStorage.multiMerge(instructions, (err) => {
|
||||
db.on('in', {
|
||||
'#': dedupid,
|
||||
ok: !err || err.length === 0,
|
||||
err,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// This returns a promise, it can be awaited!
|
||||
const reset = () => AsyncStorage.clear();
|
||||
|
||||
export default {
|
||||
read,
|
||||
write,
|
||||
reset,
|
||||
};
|
1215
examples/react-native/src/extensions/sea.js
vendored
Normal file
14
examples/react-native/src/webview-crypto/MainWorker.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
export default class MainWorker {
|
||||
private sendToWebView;
|
||||
private debug;
|
||||
readonly crypto: Crypto;
|
||||
private readonly subtle;
|
||||
private static uuid;
|
||||
private toSend;
|
||||
private readyToSend;
|
||||
private messages;
|
||||
constructor(sendToWebView: (message: string) => void, debug?: boolean);
|
||||
onWebViewMessage(message: string): void;
|
||||
private getRandomValues;
|
||||
private callMethod;
|
||||
}
|
170
examples/react-native/src/webview-crypto/MainWorker.js
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
import serializeError from 'serialize-error';
|
||||
import { parse, stringify } from './serializeBinary';
|
||||
const SUBTLE_METHODS = [
|
||||
'encrypt',
|
||||
'decrypt',
|
||||
'sign',
|
||||
'verify',
|
||||
'digest',
|
||||
'generateKey',
|
||||
'deriveKey',
|
||||
'deriveBits',
|
||||
'importKey',
|
||||
'exportKey',
|
||||
'wrapKey',
|
||||
'unwrapKey',
|
||||
];
|
||||
/*
|
||||
MainWorker provides a `crypto` attribute that proxies method calls
|
||||
to the webview.
|
||||
|
||||
It sends strings to the webview in the format:
|
||||
|
||||
{
|
||||
id: <id>,
|
||||
method: getRandomValues | subtle.<method name>,
|
||||
args: [<serialized arg>]
|
||||
}
|
||||
|
||||
When the webview succeeds in completeing that method, it gets backs:
|
||||
|
||||
{
|
||||
id: <id>,
|
||||
value: <serialized return value>
|
||||
}
|
||||
|
||||
And when it fails:
|
||||
|
||||
{
|
||||
id: <id>,
|
||||
reason: <serialized rejected reason>,
|
||||
}
|
||||
|
||||
*/
|
||||
export default class MainWorker {
|
||||
// sendToWebView should take a string and send that message to the webview
|
||||
constructor(sendToWebView, debug = false) {
|
||||
this.sendToWebView = sendToWebView;
|
||||
this.debug = debug;
|
||||
// hold a queue of messages to send, in case someone calls crypto
|
||||
// before the webview is initialized
|
||||
this.toSend = [];
|
||||
this.readyToSend = false;
|
||||
// Holds the `resolve` and `reject` function for all the promises
|
||||
// we are working on
|
||||
this.messages = {};
|
||||
}
|
||||
get crypto() {
|
||||
const callMethod = this.callMethod;
|
||||
return {
|
||||
subtle: this.subtle,
|
||||
getRandomValues: this.getRandomValues.bind(this),
|
||||
fake: true,
|
||||
};
|
||||
}
|
||||
get subtle() {
|
||||
const s = {};
|
||||
for (const m of SUBTLE_METHODS) {
|
||||
s[m] = (...args) => {
|
||||
return this.callMethod(`subtle.${m}`, args, true);
|
||||
};
|
||||
}
|
||||
return s;
|
||||
}
|
||||
// http://stackoverflow.com/a/105074/907060
|
||||
static uuid() {
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
return `${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}`;
|
||||
}
|
||||
onWebViewMessage(message) {
|
||||
// first message just tells us the webview is ready
|
||||
if (!this.readyToSend) {
|
||||
if (this.debug) {
|
||||
console.log('[webview-crypto] Got first message; ready to send');
|
||||
}
|
||||
this.readyToSend = true;
|
||||
for (const m of this.toSend) {
|
||||
this.sendToWebView(m);
|
||||
}
|
||||
return;
|
||||
}
|
||||
parse(message)
|
||||
.then(({ id, value, reason }) => {
|
||||
if (this.debug) {
|
||||
console.log('[webview-crypto] Received message:', JSON.stringify({
|
||||
id,
|
||||
value,
|
||||
reason,
|
||||
}));
|
||||
}
|
||||
if (!id) {
|
||||
console.warn('[webview-crypto] no ID passed back from message:', JSON.stringify(serializeError(reason)));
|
||||
return;
|
||||
}
|
||||
const { resolve, reject } = this.messages[id];
|
||||
if (value) {
|
||||
resolve(value);
|
||||
}
|
||||
else {
|
||||
reject(reason);
|
||||
}
|
||||
delete this.messages[id];
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.warn('[webview-crypto] error in `parse` of message:', JSON.stringify(message), 'reason:', JSON.stringify(serializeError(reason)));
|
||||
});
|
||||
}
|
||||
getRandomValues(array) {
|
||||
const promise = this.callMethod('getRandomValues', [array], false);
|
||||
// make the _promise not enumerable so it isn't JSON stringified,
|
||||
// which could lead to an infinite loop with Angular's zone promises
|
||||
Object.defineProperty(array, '_promise', {
|
||||
value: promise,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
});
|
||||
promise.then((updatedArray) => {
|
||||
array.set(updatedArray);
|
||||
});
|
||||
return array;
|
||||
}
|
||||
callMethod(method, args, waitForArrayBufferView) {
|
||||
const id = MainWorker.uuid();
|
||||
// store this promise, so we can resolve it when we get a message
|
||||
// back from the web view
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.messages[id] = { resolve, reject };
|
||||
});
|
||||
const payloadObject = { method, id, args };
|
||||
if (this.debug) {
|
||||
console.log('[webview-crypto] Sending message:', JSON.stringify({
|
||||
method,
|
||||
args,
|
||||
payloadObject,
|
||||
}));
|
||||
}
|
||||
stringify(payloadObject, waitForArrayBufferView)
|
||||
.then((message) => {
|
||||
if (this.readyToSend) {
|
||||
this.sendToWebView(message);
|
||||
}
|
||||
else {
|
||||
this.toSend.push(message);
|
||||
}
|
||||
})
|
||||
.catch((reason) => {
|
||||
this.messages[id].reject({
|
||||
message: `exception in stringify-ing message: ${method} ${id}`,
|
||||
reason,
|
||||
});
|
||||
delete this.messages[id];
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=MainWorker.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"file":"MainWorker.js","sourceRoot":"","sources":["../src/MainWorker.ts"],"names":[],"mappings":"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"}
|
6
examples/react-native/src/webview-crypto/WebViewWorker.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export declare class WebViewWorker {
|
||||
private sendToMain;
|
||||
constructor(sendToMain: (message: string) => void);
|
||||
onMainMessage(message: string): Promise<void>;
|
||||
send(data: any): Promise<void>;
|
||||
}
|
75
examples/react-native/src/webview-crypto/WebViewWorker.js
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
import serializeError from 'serialize-error';
|
||||
import { subtle } from './compat';
|
||||
import { parse, stringify } from './serializeBinary';
|
||||
export class WebViewWorker {
|
||||
constructor(sendToMain) {
|
||||
this.sendToMain = sendToMain;
|
||||
sendToMain('We are ready!');
|
||||
}
|
||||
onMainMessage(message) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let id;
|
||||
let method;
|
||||
let args;
|
||||
try {
|
||||
({ id, method, args } = yield parse(message));
|
||||
}
|
||||
catch (e) {
|
||||
yield this.send({
|
||||
reason: `Couldn't parse data: ${e}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
let value;
|
||||
try {
|
||||
if (method === 'getRandomValues') {
|
||||
value = crypto.getRandomValues(args[0]);
|
||||
}
|
||||
else {
|
||||
const methodName = method.split('.')[1];
|
||||
console.log(methodName, args);
|
||||
value = yield subtle()[methodName].apply(subtle(), args);
|
||||
// if we import a crypto key, we want to save how we imported it
|
||||
// so we can send that back and re-create the key later
|
||||
if (methodName === 'importKey') {
|
||||
value._import = {
|
||||
format: args[0],
|
||||
keyData: args[1],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
yield this.send({ id, reason: serializeError(e) });
|
||||
return;
|
||||
}
|
||||
yield this.send({ id, value });
|
||||
});
|
||||
}
|
||||
send(data) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let message;
|
||||
try {
|
||||
message = yield stringify(data);
|
||||
}
|
||||
catch (e) {
|
||||
const newData = {
|
||||
id: data.id,
|
||||
reason: `stringify error ${e}`,
|
||||
};
|
||||
this.sendToMain(JSON.stringify(newData));
|
||||
return;
|
||||
}
|
||||
this.sendToMain(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=WebViewWorker.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"file":"WebViewWorker.js","sourceRoot":"","sources":["../src/WebViewWorker.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,cAAc,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,OAAO,aAAa;IACzB,YAAoB,UAAqC;QAArC,eAAU,GAAV,UAAU,CAA2B;QACxD,UAAU,CAAC,eAAe,CAAC,CAAC;IAC7B,CAAC;IAEK,aAAa,CAAC,OAAe;;YAClC,IAAI,EAAU,CAAC;YACf,IAAI,MAAc,CAAC;YACnB,IAAI,IAAW,CAAC;YAChB,IAAI;gBACH,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;aAC9C;YAAC,OAAO,CAAC,EAAE;gBACX,MAAM,IAAI,CAAC,IAAI,CAAC;oBACf,MAAM,EAAE,wBAAwB,CAAC,EAAE;iBACnC,CAAC,CAAC;gBACH,OAAO;aACP;YACD,IAAI,KAAK,CAAC;YAEV,IAAI;gBACH,IAAI,MAAM,KAAK,iBAAiB,EAAE;oBACjC,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;iBACxC;qBAAM;oBACN,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;oBACxC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;oBAC9B,KAAK,GAAG,MAAO,MAAM,EAAU,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;oBAElE,gEAAgE;oBAChE,uDAAuD;oBACvD,IAAI,UAAU,KAAK,WAAW,EAAE;wBAC/B,KAAK,CAAC,OAAO,GAAG;4BACf,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;4BACf,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;yBAChB,CAAC;qBACF;iBACD;aACD;YAAC,OAAO,CAAC,EAAE;gBACX,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAG,cAAsB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC5D,OAAO;aACP;YACD,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAChC,CAAC;KAAA;IAEK,IAAI,CAAC,IAAS;;YACnB,IAAI,OAAe,CAAC;YACpB,IAAI;gBACH,OAAO,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;aAChC;YAAC,OAAO,CAAC,EAAE;gBACX,MAAM,OAAO,GAAG;oBACf,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,MAAM,EAAE,mBAAmB,CAAC,EAAE;iBAC9B,CAAC;gBACF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBACzC,OAAO;aACP;YACD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;KAAA;CACD"}
|
8
examples/react-native/src/webview-crypto/asyncSerialize.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
export interface ISerializer<T, S> {
|
||||
id: string;
|
||||
isType: (o: any) => boolean;
|
||||
toObject?: (t: T) => Promise<S>;
|
||||
fromObject?: (o: S) => Promise<T>;
|
||||
}
|
||||
export declare function toObjects(serializers: Array<ISerializer<any, any>>, o: any): Promise<any>;
|
||||
export declare function fromObjects(serializers: Array<ISerializer<any, any>>, o: any): Promise<any>;
|
56
examples/react-native/src/webview-crypto/asyncSerialize.js
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
// tslint:disable
|
||||
import find from 'lodash/find';
|
||||
class Serialized {
|
||||
}
|
||||
function isSerialized(object) {
|
||||
return object.hasOwnProperty('__serializer_id');
|
||||
}
|
||||
export function toObjects(serializers, o) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof o !== 'object') {
|
||||
return o;
|
||||
}
|
||||
const serializer = find(serializers, (s) => s.isType(o));
|
||||
if (serializer) {
|
||||
const value = serializer.toObject ? yield serializer.toObject(o) : o;
|
||||
return {
|
||||
__serializer_id: serializer.id,
|
||||
value: yield toObjects(serializers, value),
|
||||
};
|
||||
}
|
||||
const newO = o instanceof Array ? [] : {};
|
||||
for (const atr in o) {
|
||||
newO[atr] = yield toObjects(serializers, o[atr]);
|
||||
}
|
||||
return newO;
|
||||
});
|
||||
}
|
||||
export function fromObjects(serializers, o) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof o !== 'object') {
|
||||
return o;
|
||||
}
|
||||
if (isSerialized(o)) {
|
||||
const value = yield fromObjects(serializers, o.value);
|
||||
const serializer = find(serializers, ['id', o.__serializer_id]) || {};
|
||||
if (serializer.fromObject) {
|
||||
return serializer.fromObject(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const newO = o instanceof Array ? [] : {};
|
||||
for (const atr in o) {
|
||||
newO[atr] = yield fromObjects(serializers, o[atr]);
|
||||
}
|
||||
return newO;
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=asyncSerialize.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"file":"asyncSerialize.js","sourceRoot":"","sources":["../src/asyncSerialize.ts"],"names":[],"mappings":";;;;;;;;AAAA,iBAAiB;AACjB,OAAO,IAAI,MAAM,aAAa,CAAC;AAS/B,MAAM,UAAU;CAIf;AAED,SAAS,YAAY,CAAC,MAAW;IAChC,OAAO,MAAM,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAgB,SAAS,CAC9B,WAAyC,EACzC,CAAM;;QAEN,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE;YAC1B,OAAO,CAAC,CAAC;SACT;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,IAAI,UAAU,EAAE;YACf,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrE,OAAO;gBACN,eAAe,EAAE,UAAU,CAAC,EAAE;gBAC9B,KAAK,EAAE,MAAM,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC;aAC5B,CAAC;SAChB;QAED,MAAM,IAAI,GAAQ,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;SACjD;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CAAA;AAED,MAAM,UAAgB,WAAW,CAChC,WAAyC,EACzC,CAAM;;QAEN,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE;YAC1B,OAAO,CAAC,CAAC;SACT;QAED,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE;YACpB,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,IAAI,EAAS,CAAC;YAC7E,IAAI,UAAU,CAAC,UAAU,EAAE;gBAC1B,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;aACpC;YACD,OAAO,KAAK,CAAC;SACb;QAED,MAAM,IAAI,GAAQ,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;SACnD;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CAAA"}
|
1
examples/react-native/src/webview-crypto/compat.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export declare function subtle(): SubtleCrypto;
|
4
examples/react-native/src/webview-crypto/compat.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export function subtle() {
|
||||
return window.crypto.subtle || window.crypto.webkitSubtle;
|
||||
}
|
||||
//# sourceMappingURL=compat.js.map
|
1
examples/react-native/src/webview-crypto/compat.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"compat.js","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,MAAM;IACrB,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,IAAK,MAAM,CAAC,MAAc,CAAC,YAAY,CAAC;AACpE,CAAC"}
|
3
examples/react-native/src/webview-crypto/index.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import MainWorker from './MainWorker';
|
||||
import webViewWorkerString from './webViewWorkerString';
|
||||
export { MainWorker, webViewWorkerString };
|
4
examples/react-native/src/webview-crypto/index.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import MainWorker from './MainWorker';
|
||||
import webViewWorkerString from './webViewWorkerString';
|
||||
export { MainWorker, webViewWorkerString };
|
||||
//# sourceMappingURL=index.js.map
|
1
examples/react-native/src/webview-crypto/index.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,CAAC"}
|
5
examples/react-native/src/webview-crypto/serializeBinary.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
export declare function parse(text: string): Promise<any>;
|
||||
export declare function stringify(value: any, waitForArrayBufferView?: boolean): Promise<string>;
|
||||
export interface IArrayBufferViewWithPromise extends ArrayBufferView {
|
||||
_promise?: Promise<ArrayBufferView>;
|
||||
}
|
160
examples/react-native/src/webview-crypto/serializeBinary.js
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
import { fromObjects, toObjects } from './asyncSerialize';
|
||||
import { subtle } from './compat';
|
||||
export function parse(text) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// need decodeURIComponent so binary strings are transfered properly
|
||||
const deocodedText = unescape(text);
|
||||
const objects = JSON.parse(deocodedText);
|
||||
return fromObjects(serializers(true), objects);
|
||||
});
|
||||
}
|
||||
export function stringify(value, waitForArrayBufferView = true) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const serialized = yield toObjects(serializers(waitForArrayBufferView), value);
|
||||
// need encodeURIComponent so binary strings are transfered properly
|
||||
const message = JSON.stringify(serialized);
|
||||
return escape(message);
|
||||
});
|
||||
}
|
||||
function serializers(waitForArrayBufferView) {
|
||||
return [
|
||||
ArrayBufferSerializer,
|
||||
ArrayBufferViewSerializer(waitForArrayBufferView),
|
||||
CryptoKeySerializer,
|
||||
];
|
||||
}
|
||||
const ArrayBufferSerializer = {
|
||||
id: 'ArrayBuffer',
|
||||
isType: (o) => o instanceof ArrayBuffer,
|
||||
// from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
|
||||
// modified to use Int8Array so that we can hold odd number of bytes
|
||||
toObject: (ab) => __awaiter(this, void 0, void 0, function* () {
|
||||
return String.fromCharCode.apply(null, new Int8Array(ab));
|
||||
}),
|
||||
fromObject: (data) => __awaiter(this, void 0, void 0, function* () {
|
||||
const buf = new ArrayBuffer(data.length);
|
||||
const bufView = new Int8Array(buf);
|
||||
for (let i = 0, strLen = data.length; i < strLen; i++) {
|
||||
bufView[i] = data.charCodeAt(i);
|
||||
}
|
||||
return buf;
|
||||
}),
|
||||
};
|
||||
function isArrayBufferViewWithPromise(obj) {
|
||||
return obj.hasOwnProperty('_promise');
|
||||
}
|
||||
// Normally we could just do `abv.constructor.name`, but in
|
||||
// JavaScriptCore, this wont work for some weird reason.
|
||||
// list from https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView
|
||||
function arrayBufferViewName(abv) {
|
||||
if (abv instanceof Int8Array) {
|
||||
return 'Int8Array';
|
||||
}
|
||||
if (abv instanceof Uint8Array) {
|
||||
return 'Uint8Array';
|
||||
}
|
||||
if (abv instanceof Uint8ClampedArray) {
|
||||
return 'Uint8ClampedArray';
|
||||
}
|
||||
if (abv instanceof Int16Array) {
|
||||
return 'Int16Array';
|
||||
}
|
||||
if (abv instanceof Uint16Array) {
|
||||
return 'Uint16Array';
|
||||
}
|
||||
if (abv instanceof Int32Array) {
|
||||
return 'Int32Array';
|
||||
}
|
||||
if (abv instanceof Uint32Array) {
|
||||
return 'Uint32Array';
|
||||
}
|
||||
if (abv instanceof Float32Array) {
|
||||
return 'Float32Array';
|
||||
}
|
||||
if (abv instanceof Float64Array) {
|
||||
return 'Float64Array';
|
||||
}
|
||||
if (abv instanceof DataView) {
|
||||
return 'DataView';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function ArrayBufferViewSerializer(waitForPromise) {
|
||||
return {
|
||||
id: 'ArrayBufferView',
|
||||
isType: ArrayBuffer.isView,
|
||||
toObject: (abv) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (waitForPromise) {
|
||||
// wait for promise to resolve if the abv was returned from getRandomValues
|
||||
if (isArrayBufferViewWithPromise(abv)) {
|
||||
yield abv._promise;
|
||||
}
|
||||
}
|
||||
return {
|
||||
name: arrayBufferViewName(abv),
|
||||
buffer: abv.buffer,
|
||||
};
|
||||
}),
|
||||
fromObject: (abvs) => __awaiter(this, void 0, void 0, function* () {
|
||||
// tslint:disable-next-line
|
||||
return eval(`new ${abvs.name}(abvs.buffer)`);
|
||||
}),
|
||||
};
|
||||
}
|
||||
function hasData(ck) {
|
||||
return ck._import !== undefined;
|
||||
}
|
||||
const CryptoKeySerializer = {
|
||||
id: 'CryptoKey',
|
||||
isType: (o) => {
|
||||
const localStr = o.toLocaleString();
|
||||
// can't use CryptoKey or constructor on WebView iOS
|
||||
const isCryptoKey = localStr === '[object CryptoKey]' || localStr === '[object Key]';
|
||||
const isCryptoKeyWithData = o._import && !o.serialized;
|
||||
return isCryptoKey || isCryptoKeyWithData;
|
||||
},
|
||||
toObject: (ck) => __awaiter(this, void 0, void 0, function* () {
|
||||
// if we already have the import serialized, just return that
|
||||
if (hasData(ck)) {
|
||||
return {
|
||||
serialized: true,
|
||||
_import: ck._import,
|
||||
type: ck.type,
|
||||
extractable: ck.extractable,
|
||||
algorithm: ck.algorithm,
|
||||
usages: ck.usages,
|
||||
};
|
||||
}
|
||||
const jwk = yield subtle().exportKey('jwk', ck);
|
||||
return {
|
||||
_import: {
|
||||
format: 'jwk',
|
||||
keyData: jwk,
|
||||
},
|
||||
serialized: true,
|
||||
algorithm: ck.algorithm,
|
||||
extractable: ck.extractable,
|
||||
usages: ck.usages,
|
||||
type: ck.type,
|
||||
};
|
||||
}),
|
||||
fromObject: (cks) => __awaiter(this, void 0, void 0, function* () {
|
||||
// if we don't have access to to a real crypto implementation, just return
|
||||
// the serialized crypto key
|
||||
if (crypto.fake) {
|
||||
const newCks = Object.assign({}, cks);
|
||||
delete newCks.serialized;
|
||||
return newCks;
|
||||
}
|
||||
return subtle().importKey(cks._import.format, cks._import.keyData, cks.algorithm, cks.extractable, cks.usages);
|
||||
}),
|
||||
};
|
||||
//# sourceMappingURL=serializeBinary.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"file":"serializeBinary.js","sourceRoot":"","sources":["../src/serializeBinary.ts"],"names":[],"mappings":"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"}
|
3
examples/react-native/src/webview-crypto/webViewWorkerString.d.ts
vendored
Normal file
2135
examples/react-native/src/webview-crypto/webViewWorkerString.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"webViewWorkerString.js","sourceRoot":"","sources":["../src/webViewWorkerString.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+M9B,CAAC;AAEF,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAq4Dd,CAAC"}
|