mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
19 |
20 | @Test
21 | public void runDetoxTests() {
22 | Detox.runTests(mActivityRule);
23 | }
24 | }
--------------------------------------------------------------------------------
/template/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/template/android/app/src/debug/java/com/helloworld/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.helloworld;
8 | import android.content.Context;
9 | import com.facebook.flipper.android.AndroidFlipperClient;
10 | import com.facebook.flipper.android.utils.FlipperUtils;
11 | import com.facebook.flipper.core.FlipperClient;
12 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
13 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
14 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
15 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
16 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
17 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
18 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
19 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
21 | import com.facebook.react.ReactInstanceManager;
22 | import com.facebook.react.bridge.ReactContext;
23 | import com.facebook.react.modules.network.NetworkingModule;
24 | import okhttp3.OkHttpClient;
25 | public class ReactNativeFlipper {
26 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
27 | if (FlipperUtils.shouldEnableFlipper(context)) {
28 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
29 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
30 | client.addPlugin(new ReactFlipperPlugin());
31 | client.addPlugin(new DatabasesFlipperPlugin(context));
32 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
33 | client.addPlugin(CrashReporterPlugin.getInstance());
34 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
35 | NetworkingModule.setCustomClientBuilder(
36 | new NetworkingModule.CustomClientBuilder() {
37 | @Override
38 | public void apply(OkHttpClient.Builder builder) {
39 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
40 | }
41 | });
42 | client.addPlugin(networkFlipperPlugin);
43 | client.start();
44 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
45 | // Hence we run if after all native modules have been initialized
46 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
47 | if (reactContext == null) {
48 | reactInstanceManager.addReactInstanceEventListener(
49 | new ReactInstanceManager.ReactInstanceEventListener() {
50 | @Override
51 | public void onReactContextInitialized(ReactContext reactContext) {
52 | reactInstanceManager.removeReactInstanceEventListener(this);
53 | reactContext.runOnNativeModulesQueueThread(
54 | new Runnable() {
55 | @Override
56 | public void run() {
57 | client.addPlugin(new FrescoFlipperPlugin());
58 | }
59 | });
60 | }
61 | });
62 | } else {
63 | client.addPlugin(new FrescoFlipperPlugin());
64 | }
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/template/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/template/android/app/src/main/java/com/helloworld/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.helloworld;
2 | import android.os.Bundle;
3 | import com.facebook.react.ReactActivity;
4 | import com.zoontek.rnbootsplash.RNBootSplash;
5 |
6 | public class MainActivity extends ReactActivity {
7 |
8 | /**
9 | * Returns the name of the main component registered from JavaScript. This is used to schedule
10 | * rendering of the component.
11 | */
12 | @Override
13 | protected String getMainComponentName() {
14 | return "HelloWorld";
15 | }
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | RNBootSplash.init(R.drawable.bootsplash, MainActivity.this); // <- display the generated bootsplash.xml drawable
21 | // over our MainActivity
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/template/android/app/src/main/java/com/helloworld/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.helloworld;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactApplication;
7 | import com.facebook.react.ReactInstanceManager;
8 | import com.facebook.react.ReactNativeHost;
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.soloader.SoLoader;
11 | import java.lang.reflect.InvocationTargetException;
12 | import java.util.List;
13 |
14 | public class MainApplication extends Application implements ReactApplication {
15 |
16 | private final ReactNativeHost mReactNativeHost =
17 | new ReactNativeHost(this) {
18 | @Override
19 | public boolean getUseDeveloperSupport() {
20 | return BuildConfig.DEBUG;
21 | }
22 |
23 | @Override
24 | protected List getPackages() {
25 | @SuppressWarnings("UnnecessaryLocalVariable")
26 | List packages = new PackageList(this).getPackages();
27 | // Packages that cannot be autolinked yet can be added manually here, for example:
28 | // packages.add(new MyReactNativePackage());
29 | return packages;
30 | }
31 |
32 | @Override
33 | protected String getJSMainModuleName() {
34 | return "index";
35 | }
36 | };
37 |
38 | @Override
39 | public ReactNativeHost getReactNativeHost() {
40 | return mReactNativeHost;
41 | }
42 |
43 | @Override
44 | public void onCreate() {
45 | super.onCreate();
46 | SoLoader.init(this, /* native exopackage */ false);
47 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
48 | }
49 |
50 | /**
51 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
52 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
53 | *
54 | * @param context
55 | * @param reactInstanceManager
56 | */
57 | private static void initializeFlipper(
58 | Context context, ReactInstanceManager reactInstanceManager) {
59 | if (BuildConfig.DEBUG) {
60 | try {
61 | /*
62 | We use reflection here to pick up the class that initializes Flipper,
63 | since Flipper library is not available in release mode
64 | */
65 | Class> aClass = Class.forName("com.helloworld.ReactNativeFlipper");
66 | aClass
67 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
68 | .invoke(null, context, reactInstanceManager);
69 | } catch (ClassNotFoundException e) {
70 | e.printStackTrace();
71 | } catch (NoSuchMethodException e) {
72 | e.printStackTrace();
73 | } catch (IllegalAccessException e) {
74 | e.printStackTrace();
75 | } catch (InvocationTargetException e) {
76 | e.printStackTrace();
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/template/android/app/src/main/res/drawable/bootsplash.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/template/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Hello App Display Name
3 |
4 |
--------------------------------------------------------------------------------
/template/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/template/android/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 10.0.2.2
5 | localhost
6 |
7 |
--------------------------------------------------------------------------------
/template/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "29.0.2"
6 | minSdkVersion = 18
7 | compileSdkVersion = 29
8 | targetSdkVersion = 29
9 | ext.kotlinVersion = '1.3.10'
10 | ext.detoxKotlinVersion = '1.3.0' // Detox' default is 1.2.0
11 | ext.detoxKotlinStdlib = 'kotlin-stdlib-jdk7' // Detox' default is kotlin-stdlib-jdk8
12 | }
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | dependencies {
18 | classpath("com.android.tools.build:gradle:3.6.2")
19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
20 |
21 |
22 | // NOTE: Do not place your application dependencies here; they belong
23 | // in the individual module build.gradle files
24 | }
25 | }
26 |
27 | allprojects {
28 | task downloadDependencies() {
29 | description 'Download all dependencies to the Gradle cache'
30 | doLast {
31 | configurations.findAll().each { config ->
32 | if (config.name.contains("minReactNative") && config.canBeResolved) {
33 | print config.name
34 | print '\n'
35 | config.files
36 | }
37 | }
38 | }
39 | }
40 | repositories {
41 | mavenLocal()
42 | maven {
43 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
44 | url("$rootDir/../node_modules/react-native/android")
45 | }
46 | maven {
47 | // Android JSC is installed from npm
48 | url("$rootDir/../node_modules/jsc-android/dist")
49 | }
50 | maven {
51 | // All of Detox' artifacts are provided via the npm module
52 | url "$rootDir/../node_modules/detox/Detox-android"
53 | }
54 | google()
55 | jcenter()
56 | maven { url 'https://www.jitpack.io' }
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/template/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.75.1
29 |
--------------------------------------------------------------------------------
/template/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/template/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/template/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=`expr $i + 1`
158 | done
159 | case $i in
160 | 0) set -- ;;
161 | 1) set -- "$args0" ;;
162 | 2) set -- "$args0" "$args1" ;;
163 | 3) set -- "$args0" "$args1" "$args2" ;;
164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=`save "$@"`
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | exec "$JAVACMD" "$@"
184 |
--------------------------------------------------------------------------------
/template/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 | @rem Execute Gradle
88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
89 |
90 | :end
91 | @rem End local scope for the variables with windows NT shell
92 | if "%ERRORLEVEL%"=="0" goto mainEnd
93 |
94 | :fail
95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
96 | rem the _cmd.exe /c_ return code!
97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
98 | exit /b 1
99 |
100 | :mainEnd
101 | if "%OS%"=="Windows_NT" endlocal
102 |
103 | :omega
104 |
--------------------------------------------------------------------------------
/template/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'HelloWorld'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 |
--------------------------------------------------------------------------------
/template/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "HelloWorld",
3 | "displayName": "HelloWorld"
4 | }
5 |
--------------------------------------------------------------------------------
/template/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 |
4 | return {
5 | presets: ['module:metro-react-native-babel-preset'],
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/template/docs/circle-ci.md:
--------------------------------------------------------------------------------
1 | # CircleCI
2 |
3 | These instructions assume you have followed the installation instructions our [`hygen-templates-echobind` repository](https://github.com/echobind/hygen-echobind-templates).
4 |
5 | ## Setup CircleCI to build
6 |
7 | After generating a new project, add a GitHub remote and push up the changes. Login to CircleCI and head to the settings for your organization.
8 |
9 | 1. Ensure you've added a `MacOS` Plan. This is required to build iOS projects.
10 |
11 | 
12 |
13 | 2. Enable "untrusted or 3rd party orbs" under the organizations security settings. This step is required to use the provided CircleCI Orb configuration for react-native.
14 | Learn more about CircleCI Orbs [here](https://circleci.com/docs/2.0/using-orbs/). The source for the react-native orb is [here](https://github.com/echobind/circleci-react-native-orb).
15 |
16 | 
17 |
18 | 3. Tell Circle CI to build this project (must be logged in with an account that has owner permissions on GitHub).
19 |
20 | 
21 |
22 | 4. Thats it! Your project should be building. Here's what the workflow looks like:
23 |
24 | 
25 |
26 | Once node tests (via jest) complete, iOS and Android builds run in parallel. See `.circleci/config.yml` for workflow details, including the Fastlane lanes that are run on each branch by default.
27 |
--------------------------------------------------------------------------------
/template/docs/hygen-templates.md:
--------------------------------------------------------------------------------
1 | # Hygen Templates
2 |
3 | Templates can be found in the `_templates` directory at the root of the project. They are customizable generators that are meant to evolve alongside your code.
4 |
5 | Out of the box, we've wired up:
6 |
7 | - A component generator (`yarn g:component Button`). This will:
8 | - Prompt you if you want a functional or stateful component
9 | - add a Storybook story
10 | - A screen generator (`yarn g:screen LoginScreen`). This will:
11 | - Create a screen file that uses a general Screen wrapper component.
12 | - A util generator (`yarn g:util happy). This will:
13 | - Create a simple util function with a corresponding jest test. This will fail by default to encourage you to write them!
14 | - An E2E generator (`yarn g:e2e "My cool e2e test"`). This will:
15 | - Create a detox test in the `e2e` directory.
16 |
17 | We maintain additional templates related to CircleCi and Fastlane in the [`hygen-echobind-templates`](https://github.com/echobind/hygen-echobind-templates). You'll find instructions in that repository on how to use them to generate configs for your project.
18 |
--------------------------------------------------------------------------------
/template/docs/navigation.md:
--------------------------------------------------------------------------------
1 | # Navigation
2 |
3 | This project uses react-navigation for navigating in the app. Out of the box, navigation consists of a few main concepts.
4 |
5 | 
6 |
7 | ## Root Nav
8 |
9 | This navigator is rendered at the root of the app. It is a [SwitchNavigator](https://reactnavigation.org/docs/en/switch-navigator.html) and has 1 main responsibility: to take a `currentUser` object and render either an [Main Nav](#app-nav) or a [Guest Nav](#guest-nav) [StackNavigator](https://reactnavigation.org/docs/en/stack-navigator.html).
10 |
11 | ## Guest Nav
12 |
13 | This nav is a [StackNavigator](https://reactnavigation.org/docs/en/stack-navigator.html) that renders when a user is logged out of your app. Guest Nav screens typically consist of:
14 |
15 | - Onboarding
16 | - Registration
17 | - Login
18 | - Forgot Password
19 |
20 | ## App Nav
21 |
22 | This nav is a [StackNavigator](https://reactnavigation.org/docs/en/stack-navigator.html) that contains only the [Main Nav](#main-nav) by default. If you have any full screen modals that should render above the main nav UI, you should put them here.
23 |
24 | ## Main Nav
25 |
26 | Main Nav is meant to be the main interface of the app. By default it renders a [BottomTabNavigator](https://reactnavigation.org/docs/en/bottom-tab-navigator.html).
27 |
28 | ## Onboarding Nav (optional)
29 |
30 | This nav is a [StackNavigator](https://reactnavigation.org/docs/en/stack-navigator.html) that is meant to handle onboarding. We keep it as a separate stack from the [Guest Nav](#guest-nav). If your app doesn't need onboarding, feel free to delete this nav.
31 |
--------------------------------------------------------------------------------
/template/docs/splashscreens.md:
--------------------------------------------------------------------------------
1 | # Splash Screens
2 |
3 | We've setup [react-native-splash-screen](https://github.com/crazycodeboy/react-native-splash-screen) for you! To customize it, follow the steps below.
4 |
5 | ## iOS
6 |
7 | 1. Create a 240px x 240px image @ 1x, 2x, and 3x sizes.
8 | 2. Open the project in Xcode.
9 | 3. Navigate to the `SplashScreen` image asset, and replace the items with your own:
10 |
11 | 
12 |
13 | 4. Update the background color or centering of the image by opening `LaunchScreen.xib` and adjusting the necessary properties:
14 |
15 | 
16 |
17 | 
18 |
19 | ## Android
20 |
21 | 1. Copy your 1x, 2x and 3x image assets into the proper mipmap folders (ldpi, xhdpi, and xxhdpi respectively)
22 |
23 | 
24 |
25 | 2. Update the color by changing the `launch_background` color in `android/app/src/main/res/values/colors.xml`.
26 |
--------------------------------------------------------------------------------
/template/e2e/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "testEnvironment": "node",
3 | "reporters": ["detox/runners/jest/streamlineReporter"],
4 | "verbose": true,
5 | "setupFilesAfterEnv": ["./init.js"]
6 | }
--------------------------------------------------------------------------------
/template/e2e/init.js:
--------------------------------------------------------------------------------
1 | /*global jasmine */
2 | /*eslint no-undef: "error"*/
3 | import detox, { device } from 'detox';
4 | import adapter from 'detox/runners/jest/adapter';
5 | import specReporter from 'detox/runners/jest/specReporter';
6 | import config from '../.detoxrc.json'; // .detox;
7 |
8 | // Set the default timeout
9 |
10 | jest.setTimeout(120000);
11 | jasmine.getEnv().addReporter(adapter);
12 |
13 | // This takes care of generating status logs on a per-spec basis. By default, jest only reports at file-level.
14 | // This is strictly optional.
15 | jasmine.getEnv().addReporter(specReporter);
16 |
17 | beforeAll(async () => {
18 | await detox.init(config.detox, { launchApp: false });
19 | await device.launchApp({
20 | /** this is where you can set what permissions might be needed */
21 | // permissions: { notifications: 'YES', camera: 'YES' },
22 | newInstance: true,
23 | });
24 | });
25 |
26 | beforeEach(async () => {
27 | await adapter.beforeEach();
28 | });
29 |
30 | afterAll(async () => {
31 | await adapter.afterAll();
32 | await detox.cleanup();
33 | });
34 |
--------------------------------------------------------------------------------
/template/e2e/introScreen.spec.js:
--------------------------------------------------------------------------------
1 | describe('MyApp', () => {
2 | beforeEach(() => {
3 | device.reloadReactNative();
4 | });
5 |
6 | it('should have intro screen', () => {
7 | expect(element(by.id('introScreenText'))).toBeVisible();
8 | expect(element(by.id('introScreenText'))).toHaveText('Welcome to the intro Screen!');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/template/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | fastlane_version '2.105.2'
2 |
3 | # before_all do
4 | # ensure_git_branch
5 | # ensure_git_status_clean
6 | # git_pull
7 | # end
8 |
9 | platform :ios do
10 | # iOS Lanes
11 | desc 'Fetch certificates and provisioning profiles'
12 | lane :certificates do
13 | match(app_identifier: 'com.HelloWorld.mobile', type: 'development', readonly: true)
14 | end
15 |
16 | desc 'generate iOS appiconset from master image'
17 | lane :icon do
18 | appicon(
19 | appicon_devices: [:iphone, :ios_marketing],
20 | appicon_path: "ios/HelloWorld/Images.xcassets"
21 | )
22 | end
23 | end
24 |
25 | platform :android do
26 | desc 'generate Android launch icon from master image'
27 | lane :icon do
28 | android_appicon(
29 | appicon_image_file: 'fastlane/metadata/app_icon_android.png',
30 | appicon_icon_types: [:launcher],
31 | appicon_path: 'android/app/src/main/res/mipmap'
32 | )
33 |
34 | android_appicon(
35 | appicon_image_file: 'fastlane/metadata/app_icon_android.png',
36 | appicon_icon_types: [:notification],
37 | appicon_path: 'android/app/src/main/res/mipmap',
38 | appicon_filename: 'ic_notification'
39 | )
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/template/fastlane/Pluginfile:
--------------------------------------------------------------------------------
1 | # Autogenerated by fastlane
2 | #
3 | # Ensure this file is checked in to source control!
4 |
5 | gem 'fastlane-plugin-appicon'
6 |
--------------------------------------------------------------------------------
/template/fastlane/README.md:
--------------------------------------------------------------------------------
1 | # fastlane documentation
2 |
3 | # Installation
4 |
5 | Make sure you have the latest version of the Xcode command line tools installed:
6 |
7 | ```
8 | xcode-select --install
9 | ```
10 |
11 | Install _fastlane_ using
12 |
13 | ```
14 | [sudo] gem install fastlane -NV
15 | ```
16 |
17 | or alternatively using `brew cask install fastlane`
18 |
19 | # Available Actions
20 |
21 | ## iOS
22 |
23 | ### ios certificates
24 |
25 | ```
26 | fastlane ios certificates
27 | ```
28 |
29 | Fetch certificates and provisioning profiles
30 |
31 | ### ios icon
32 |
33 | ```
34 | fastlane ios icon
35 | ```
36 |
37 | generate iOS appiconset from master image
38 |
39 | ---
40 |
41 | ## Android
42 |
43 | ### android icon
44 |
45 | ```
46 | fastlane android icon
47 | ```
48 |
49 | generate Android launch icon from master image
50 |
51 | ---
52 |
53 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
54 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
55 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
56 |
--------------------------------------------------------------------------------
/template/fastlane/metadata/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/fastlane/metadata/app_icon.png
--------------------------------------------------------------------------------
/template/fastlane/metadata/app_icon_android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/fastlane/metadata/app_icon_android.png
--------------------------------------------------------------------------------
/template/husky.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | hooks: {
3 | 'pre-commit': 'lint-staged',
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/template/index.js:
--------------------------------------------------------------------------------
1 | import { AppRegistry } from 'react-native';
2 |
3 | // Recommended in react-navigation v5 installation
4 | // https://reactnavigation.org/docs/getting-started/
5 | import 'react-native-gesture-handler';
6 |
7 | import App from './src/App';
8 | import { name as appName } from './app.json';
9 |
10 | AppRegistry.registerComponent(appName, () => App);
11 |
--------------------------------------------------------------------------------
/template/ios/Dummy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dummy.swift
3 | // HelloWorld
4 | //
5 | // Created by Ryan Atkinson on 6/3/20.
6 | //
7 |
8 | import Foundation
9 |
--------------------------------------------------------------------------------
/template/ios/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6 |
7 | # gem "rails"
8 | gem "cocoapods", "1.8.4"
--------------------------------------------------------------------------------
/template/ios/HelloWorld-Bridging-Header.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/ios/HelloWorld-Bridging-Header.h
--------------------------------------------------------------------------------
/template/ios/HelloWorld.xcodeproj/xcshareddata/xcschemes/HelloWorld.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/template/ios/HelloWorld/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : UIResponder
5 |
6 | @property (nonatomic, strong) UIWindow *window;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/template/ios/HelloWorld/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import
6 | #import "RNBootSplash.h"
7 |
8 | #ifdef FB_SONARKIT_ENABLED
9 | #import
10 | #import
11 | #import
12 | #import
13 | #import
14 | #import
15 |
16 | static void InitializeFlipper(UIApplication *application) {
17 | FlipperClient *client = [FlipperClient sharedClient];
18 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
19 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
20 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
21 | [client addPlugin:[FlipperKitReactPlugin new]];
22 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
23 | [client start];
24 | }
25 | #endif
26 |
27 | @implementation AppDelegate
28 |
29 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
30 | {
31 | #ifdef FB_SONARKIT_ENABLED
32 | InitializeFlipper(application);
33 | #endif
34 |
35 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
36 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
37 | moduleName:@"HelloWorld"
38 | initialProperties:nil];
39 |
40 | if (@available(iOS 13.0, *)) {
41 | rootView.backgroundColor = [UIColor systemBackgroundColor];
42 | } else {
43 | rootView.backgroundColor = [UIColor whiteColor];
44 | }
45 |
46 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
47 | UIViewController *rootViewController = [UIViewController new];
48 | rootViewController.view = rootView;
49 | [RNBootSplash initWithStoryboard:@"LaunchScreen" rootView:rootView];
50 | self.window.rootViewController = rootViewController;
51 | [self.window makeKeyAndVisible];
52 | return YES;
53 | }
54 |
55 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
56 | {
57 | #if DEBUG
58 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
59 | #else
60 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
61 | #endif
62 | }
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/template/ios/HelloWorld/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/template/ios/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/template/ios/HelloWorld/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/template/ios/HelloWorld/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSExceptionDomains
32 |
33 | localhost
34 |
35 | NSExceptionAllowsInsecureHTTPLoads
36 |
37 |
38 |
39 |
40 | NSLocationWhenInUseUsageDescription
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 | UIViewControllerBasedStatusBarAppearance
55 |
56 | UIAppFonts
57 |
58 | FontAwesome.ttf
59 | MaterialIcons.ttf
60 | SimpleLineIcons.ttf
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/template/ios/HelloWorld/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/template/ios/HelloWorld/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/template/ios/HelloWorldTests/HelloWorldTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface HelloWorldTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation HelloWorldTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
38 | if (level >= RCTLogLevelError) {
39 | redboxError = message;
40 | }
41 | });
42 | #endif
43 |
44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 |
48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
50 | return YES;
51 | }
52 | return NO;
53 | }];
54 | }
55 |
56 | #ifdef DEBUG
57 | RCTSetLogFunction(RCTDefaultLogFunction);
58 | #endif
59 |
60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
62 | }
63 |
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/template/ios/HelloWorldTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/template/ios/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../node_modules/react-native/scripts/react_native_pods'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | platform :ios, '10.0'
5 |
6 | target 'HelloWorld' do
7 | config = use_native_modules!
8 |
9 | use_react_native!(:path => config["reactNativePath"])
10 |
11 | target 'HelloWorldTests' do
12 | inherit! :complete
13 | # Pods for testing
14 | end
15 |
16 | # Enables Flipper.
17 | #
18 | # Note that if you have use_frameworks! enabled, Flipper will not work and
19 | # you should disable these next few lines.
20 | use_flipper!({ 'Flipper' => '0.75.1' })
21 | post_install do |installer|
22 | flipper_post_install(installer)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/template/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 |
8 | module.exports = {
9 | transformer: {
10 | getTransformOptions: async () => ({
11 | transform: {
12 | experimentalImportSupport: false,
13 | inlineRequires: false,
14 | },
15 | }),
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello-world",
3 | "version": "0.0.1",
4 | "description": "a React Native app bootstrapped with the Echobind template",
5 | "private": true,
6 | "scripts": {
7 | "start": "react-native start --reset-cache",
8 | "test": "jest",
9 | "test:ci": "jest",
10 | "test:watch": "jest --changedSince=master --watch",
11 | "lint": "eslint . --ext .ts,.tsx",
12 | "g:component": "hygen component new --name",
13 | "g:screen": "hygen screen new --name",
14 | "g:util": "hygen util new --name",
15 | "g:e2e": "hygen e2e new --name",
16 | "ios": "react-native run-ios --simulator='iPhone 11'",
17 | "bundle:ios": "react-native bundle --entry-file='index.js' --bundle-output='./ios/main.jsbundle' --dev=false --platform='ios' --assets-dest='./ios'",
18 | "preandroid": "adb reverse tcp:3030 tcp:3030 && adb reverse tcp:9090 tcp:9090",
19 | "android": "react-native run-android",
20 | "android:clean": "cd android && ./gradlew clean && cd ..",
21 | "simulators": "xcrun simctl list",
22 | "emulators": "emulator -list-avds",
23 | "emulator:start": "emulator -avd",
24 | "e2e:ios-debug": "detox build -c ios.sim.debug && detox test -c ios.sim.debug --cleanup",
25 | "e2e:ios": "detox build -c ios.sim.release && detox test -c ios.sim.release --cleanup",
26 | "e2e:android-debug": "detox build -c android.emu.debug && detox test -c android.emu.debug -l trace --cleanup",
27 | "e2e:android": "detox build -c android.emu.release && detox test -c android.emu.release -l trace --cleanup"
28 | },
29 | "keywords": [],
30 | "author": "",
31 | "license": "ISC",
32 | "dependencies": {
33 | "@emotion/core": "^10.0.6",
34 | "@emotion/native": "^10.0.6",
35 | "@react-native-community/masked-view": "^0.1.10",
36 | "@react-navigation/bottom-tabs": "^5.6.1",
37 | "@react-navigation/native": "^5.6.1",
38 | "@react-navigation/stack": "^5.6.2",
39 | "emotion-theming": "^10.0.19",
40 | "react": "16.13.1",
41 | "react-native": "0.63.2",
42 | "react-native-bootsplash": "^3.1.2",
43 | "react-native-elements": "^1.2.7",
44 | "react-native-gesture-handler": "^1.6.1",
45 | "react-native-reanimated": "^1.9.0",
46 | "react-native-safe-area-context": "^3.0.7",
47 | "react-native-safe-area-view": "^1.0.0",
48 | "react-native-screens": "^2.9.0",
49 | "react-native-vector-icons": "6.6.0",
50 | "styled-system": "^5.1.2"
51 | },
52 | "devDependencies": {
53 | "@babel/core": "^7.8.4",
54 | "@babel/runtime": "^7.8.4",
55 | "@react-native-community/eslint-config": "^1.1.0",
56 | "metro-react-native-babel-preset": "^0.59.0",
57 | "react-test-renderer": "16.13.1",
58 | "@storybook/addon-ondevice-backgrounds": "^5.2.5",
59 | "@storybook/addons": "^5.2.5",
60 | "@storybook/react-native": "^5.3.0-rc.1",
61 | "@types/jest": "^24.0.18",
62 | "@types/node": "^12.7.7",
63 | "@types/react": "^16.9.0",
64 | "@types/react-native": "^0.60.15",
65 | "babel-jest": "^25.1.0",
66 | "detox": "^17.14.1",
67 | "enquirer": "^2.3.0",
68 | "eslint": "^6.6.0",
69 | "eslint-config-prettier": "^6.5.0",
70 | "eslint-plugin-echobind": "^0.2.0",
71 | "eslint-plugin-prettier": "^3.1.1",
72 | "husky": "^1.2.1",
73 | "hygen": "^2.0.4",
74 | "jest": "^25.1.0",
75 | "lint-staged": "^10.0.0-beta.3",
76 | "prettier": "^1.19.1",
77 | "react-native-flipper": "^0.73.0",
78 | "solidarity": "^2.3.1",
79 | "ts-jest": "^23.10.5",
80 | "typescript": "^3.2.2",
81 | "typescript-styled-plugin": "^0.13.0"
82 | },
83 | "jest": {
84 | "preset": "react-native",
85 | "moduleFileExtensions": [
86 | "ts",
87 | "tsx",
88 | "js"
89 | ],
90 | "globals": {
91 | "ts-jest": {
92 | "tsConfig": {
93 | "isolatedModules": false
94 | }
95 | }
96 | },
97 | "transform": {
98 | "^.+\\.(js)$": "/node_modules/react-native/jest/preprocessor.js",
99 | "\\.(ts|tsx)$": "ts-jest"
100 | },
101 | "testRegex": "(/__tests__/.*|(test|spec))\\.(ts|tsx|js)$",
102 | "testPathIgnorePatterns": [
103 | "\\.snap$",
104 | "e2e",
105 | "/node_modules/"
106 | ],
107 | "cacheDirectory": ".jest/cache"
108 | },
109 | "lint-staged": {
110 | "**/*.{ts,tsx}": [
111 | "yarn lint --fix",
112 | "git add"
113 | ]
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/template/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | assets: ['./src/assets/fonts/'],
3 | };
4 |
--------------------------------------------------------------------------------
/template/rn-cli.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // should only be required for < 0.57
3 | // getTransformModulePath() {
4 | // return require.resolve('react-native-typescript-transformer');
5 | // },
6 | getSourceExts() {
7 | // allow overrides via ENV vars (ex: component.e2e.js)
8 | return process.env.RN_SRC_EXT
9 | ? process.env.RN_SRC_EXT.split(',').concat(['ts', 'tsx'])
10 | : ['ts', 'tsx'];
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/template/setup/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const { execSync } = require('child_process');
4 | const { prompt } = require('enquirer');
5 |
6 | async function setup() {
7 | const deleteFile = fileName => fs.unlinkSync(path.join(__dirname, fileName));
8 | const deleteDirectory = dirName => fs.rmdirSync(path.join(__dirname, dirName));
9 | const writeFile = (fileName, data) => fs.writeFileSync(path.join(__dirname, fileName), data);
10 |
11 | console.log('\n🤔 Checking system setup and prerequisites...');
12 | try {
13 | execSync('yarn solidarity --verbose', { stdio: 'inherit' });
14 | } catch {
15 | // Bail if solidarity checks fail
16 | return;
17 | }
18 |
19 | console.log('\n📝 Configuring project display name and bundle identifier...');
20 | const { displayName } = await prompt({
21 | type: 'input',
22 | name: 'displayName',
23 | message: 'App Display Name (ProjectName):',
24 | initial: 'HelloWorld',
25 | });
26 |
27 | const { bundleIdentifer } = await prompt({
28 | type: 'input',
29 | name: 'bundleIdentifer',
30 | message: 'App Bundle Identifier',
31 | initial: displayName.toLowerCase() || 'helloworld',
32 | });
33 |
34 | const { confirmed } = await prompt({
35 | type: 'confirm',
36 | name: 'confirmed',
37 | message: `Continue with => App Display Name: ${displayName} | App Bundle Identifer: ${bundleIdentifer}`,
38 | });
39 |
40 | if (!confirmed) {
41 | console.log('\n Cannot continue without choosing a display name and app bundle identifier...');
42 | console.log('\n Exiting setup...');
43 | return;
44 | }
45 |
46 | const { shouldInitializeGitRepository } = await prompt({
47 | type: 'confirm',
48 | name: 'shouldInitializeGitRepository',
49 | message: 'Would you like to initialize this project as a git repository?',
50 | });
51 |
52 | console.log('🔄 Setting up...');
53 | console.log('\n🛠 Setting up fastlane and installing app icons...');
54 | const rootDirectory = path.join(__dirname, '../');
55 | execSync('bundle install', { cwd: rootDirectory });
56 | execSync('bundle exec fastlane ios icon', { cwd: rootDirectory });
57 | execSync('bundle exec fastlane android icon', { cwd: rootDirectory });
58 |
59 | console.log('\n Updating project display name and bundle identifier...');
60 | execSync(`npx react-native-rename ${displayName} -b ${bundleIdentifer}`, {
61 | cwd: rootDirectory,
62 | });
63 |
64 | execSync(
65 | `bundle exec fastlane run update_app_identifier app_identifier:"${bundleIdentifer}" plist_path:"${displayName}/Info.plist" xcodeproj:"ios/${displayName}.xcodeproj"`,
66 | { cwd: rootDirectory },
67 | );
68 |
69 | console.log('\n☕ Installing Cocoapods...');
70 | execSync('bundle install', { cwd: 'ios' });
71 | execSync('bundle exec pod install', { cwd: 'ios' });
72 |
73 | execSync(
74 | `HYGEN_OVERWRITE=1 yarn hygen setup splashscreen ios --displayName ${displayName} --bundleIdentifer ${bundleIdentifer}`,
75 | );
76 |
77 | console.log('\n🤖🌊 Setting up android splash screens...');
78 | execSync(
79 | `HYGEN_OVERWRITE=1 yarn hygen setup splashscreen android --displayName ${displayName} --bundleIdentifer ${bundleIdentifer}`,
80 | );
81 |
82 | console.log('\n🗑 Removing cruft...');
83 | execSync('rm -rf setup', { cwd: rootDirectory });
84 | execSync('rm -rf .git', { cwd: rootDirectory }); // blow away old repo if there
85 |
86 | if (shouldInitializeGitRepository) {
87 | console.log('\n📝 Committing project...');
88 | execSync(
89 | 'rm -rf .git && git init && git add . && git commit -m "Initialize new React Native project."',
90 | {
91 | cwd: rootDirectory,
92 | },
93 | );
94 |
95 | console.log('\n📱 Setting initial version @0.0.1 ...');
96 | execSync('npx react-native-version --never-increment-build', {
97 | cwd: rootDirectory,
98 | });
99 |
100 | console.log('\n📝 Committing changes...');
101 | execSync('git add . && git commit -m "Set proper initial symver version"', {
102 | cwd: rootDirectory,
103 | });
104 | }
105 |
106 | console.log(`\n✅ Setup completed!`);
107 | console.log('\n\n------------------------');
108 | console.log('** PostInstall Notes: **');
109 | console.log('------------------------\n\n');
110 | console.log('\n* Check out the docs folder to customize and finalize your app!');
111 | console.log('\n');
112 | }
113 |
114 | setup();
115 |
--------------------------------------------------------------------------------
/template/splashscreenPreview/SplashScreens-iPhone12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/splashscreenPreview/SplashScreens-iPhone12.gif
--------------------------------------------------------------------------------
/template/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement, useEffect } from 'react';
2 | import RNBootSplash from 'react-native-bootsplash';
3 | import { SafeAreaProvider } from 'react-native-safe-area-context';
4 | import { NavigationContainer } from '@react-navigation/native';
5 | import { ThemeProvider } from 'emotion-theming';
6 |
7 | import Storybook from '../storybook';
8 | import { AppNav } from './navigation/AppNav';
9 | import { GuestNav } from './navigation/GuestNav';
10 |
11 | import { theme } from './styles';
12 |
13 | // NOTE: Change this boolean to true to render the Storybook view for development!
14 | const RENDER_STORYBOOK = false;
15 |
16 | const App = (): ReactElement | null => {
17 | // hide the splashscreen on mount
18 | useEffect(() => {
19 | RNBootSplash.hide({ fade: true });
20 | }, []);
21 |
22 | const renderNavigation = (): ReactElement => {
23 | // replace with actual user profile data once loaded, or fallback to guest nav
24 | const currentUser = false;
25 |
26 | return currentUser ? : ;
27 | };
28 |
29 | return (
30 |
31 |
32 |
33 | {RENDER_STORYBOOK ? : renderNavigation()}
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | export default App;
41 |
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-Bold.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-BoldItalic.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-Italic.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-Medium.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-MediumItalic.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-Regular.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-SemiBold.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Archivo-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Archivo-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/template/src/assets/fonts/Blockletter.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/fonts/Blockletter.otf
--------------------------------------------------------------------------------
/template/src/assets/fonts/README.md:
--------------------------------------------------------------------------------
1 | Add fonts here and run `react-native link`
2 |
--------------------------------------------------------------------------------
/template/src/assets/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/images/background.png
--------------------------------------------------------------------------------
/template/src/assets/images/background@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/images/background@2x.png
--------------------------------------------------------------------------------
/template/src/assets/images/background@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/echobind/react-native-template/d4dc46369d6162f48264266fe4b953f7deff8c72/template/src/assets/images/background@3x.png
--------------------------------------------------------------------------------
/template/src/components/Button/Button.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/react-native';
2 | import React from 'react';
3 |
4 | import { Button } from './Button';
5 |
6 | storiesOf('components/Button', module).add('Default', () => (
7 |
8 | ));
9 |
--------------------------------------------------------------------------------
/template/src/components/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { ActivityIndicator } from 'react-native';
3 | import {
4 | backgroundColor,
5 | BorderProps,
6 | ColorProps,
7 | SpaceProps,
8 | FlexProps,
9 | LayoutProps,
10 | } from 'styled-system';
11 |
12 | import { Container } from '../Container';
13 | import { Text } from '../Text';
14 | import { Touchable } from '../Touchable';
15 | import { colors } from '../../styles';
16 | import { AccessbilityRole } from '../../types/AccessibilityRole';
17 |
18 | interface ButtonProps {
19 | /**
20 | * Overrides the text that's read by the screen reader when the user interacts with the element. By default, the
21 | * label is constructed by traversing all the children and accumulating all the Text nodes separated by space.
22 | */
23 | accessibilityLabel: string;
24 | /** Accessibility Role tells a person using either VoiceOver on iOS or TalkBack on Android the type of element that is focused on. */
25 | accessbilityRole?: AccessbilityRole;
26 | /** disabled button state */
27 | disabled?: boolean;
28 | /** loading button state */
29 | loading?: boolean;
30 | /** the text label of the button */
31 | label: string;
32 | /** the callback to be invoked onPress */
33 | onPress: () => void;
34 | }
35 |
36 | type ComponentProps = ButtonProps & BorderProps & ColorProps & SpaceProps & FlexProps & LayoutProps;
37 |
38 | /**
39 | * notes:
40 | * - restricting inner text style from being directly configurable to avoid style prop conflicts
41 | * - if button is disabled it will not render a touchableOpacity at all
42 | */
43 | export const Button: FC = ({
44 | accessibilityLabel,
45 | label,
46 | onPress,
47 | disabled,
48 | loading,
49 | color: componentColor,
50 | ...props
51 | }) => {
52 | const ButtonContainer = disabled ? Container : Touchable;
53 | const onPressAction = loading ? null : onPress;
54 |
55 | return (
56 |
67 | {loading ? (
68 |
69 | ) : (
70 |
71 | {label}
72 |
73 | )}
74 |
75 | );
76 | };
77 |
78 | Button.defaultProps = {
79 | disabled: false,
80 | borderColor: colors.transparent,
81 | borderWidth: 1,
82 | backgroundColor: colors.orange,
83 | };
84 |
--------------------------------------------------------------------------------
/template/src/components/Button/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Button';
--------------------------------------------------------------------------------
/template/src/components/Container/Container.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react-native';
3 |
4 | import { Container } from './Container';
5 |
6 | storiesOf('components/Container', module).add('Default', () => );
7 |
--------------------------------------------------------------------------------
/template/src/components/Container/Container.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | BorderProps,
3 | borders,
4 | color,
5 | ColorProps,
6 | flexbox,
7 | FlexProps,
8 | layout,
9 | space,
10 | SpaceProps,
11 | } from 'styled-system';
12 | import styled from '@emotion/native';
13 |
14 | import { margins } from '../../styles';
15 |
16 | interface ContainerProps {
17 | /** applies "flex: 1" style */
18 | fill?: boolean;
19 | /** applies "width: 100%" style */
20 | fullWidth?: boolean;
21 | /** centers content both vertically and horizontally */
22 | centerContent?: boolean;
23 | /**
24 | * applies default horizontal screen margins.
25 | * decoupled from Screen component to make layout-building more flexible.
26 | */
27 | screenMargins?: boolean;
28 | }
29 |
30 | type ComponentProps = ContainerProps & BorderProps & ColorProps & FlexProps & SpaceProps;
31 |
32 | /**
33 | * This is our primitive View component with styled-system props applied
34 | */
35 | export const Container = styled.View`
36 | ${space};
37 | ${color};
38 | ${borders};
39 | ${layout};
40 | ${flexbox};
41 | ${props =>
42 | props.fill &&
43 | `
44 | flex: 1;
45 | `}
46 | ${props =>
47 | props.fullWidth &&
48 | `
49 | width: 100%;
50 | `}
51 | ${props =>
52 | props.centerContent &&
53 | `
54 | justifyContent: center;
55 | alignItems: center;
56 | `}
57 | ${props =>
58 | props.screenMargins &&
59 | `
60 | paddingHorizontal: ${margins.screenMargin};
61 | `}
62 | `;
63 |
64 | Container.defaultProps = {};
65 |
--------------------------------------------------------------------------------
/template/src/components/Container/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Container';
2 |
--------------------------------------------------------------------------------
/template/src/components/Login/Login.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/react-native';
2 | import React from 'react';
3 |
4 | import { Login } from './Login';
5 | import { Screen } from '../Screen';
6 | import { colors } from '../../styles';
7 |
8 | const ScreenDecorator = storyFn => {storyFn()} ;
9 |
10 | storiesOf('components/Login', module)
11 | .addDecorator(ScreenDecorator)
12 | .add('Default', () => (
13 | console.log('login pressed')}
15 | forgotPasswordPress={() => console.log('forgot password pressed')}
16 | registrationPress={() => console.log('registration pressed')}
17 | />
18 | ));
19 |
--------------------------------------------------------------------------------
/template/src/components/Login/Login.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { BorderProps, ColorProps, SpaceProps, FlexProps } from 'styled-system';
3 | import { Icon } from 'react-native-elements';
4 | import { Button } from '../Button';
5 | import { Text } from '../Text';
6 | import { TextInput } from '../TextInput';
7 | import { Touchable } from '../Touchable';
8 | import { Container } from '../Container';
9 |
10 | import { colors } from '../../styles';
11 |
12 | interface LoginProps {
13 | /** the callbacks to be invoked onPress */
14 | loginPress: () => void;
15 | registrationPress: () => void;
16 | forgotPasswordPress: () => void;
17 | }
18 |
19 | type ComponentProps = LoginProps & FlexProps & SpaceProps & BorderProps & ColorProps;
20 |
21 | export const Login: FC = ({
22 | loginPress,
23 | registrationPress,
24 | forgotPasswordPress,
25 | children,
26 | ...props
27 | }) => {
28 | return (
29 |
41 |
42 | Welcome, please {'\n'}sign in.
43 |
44 | }
49 | marginTop={2}
50 | borderRadius={5}
51 | borderColor={colors.lightGray}
52 | />
53 | }
58 | marginTop={2}
59 | borderRadius={5}
60 | borderColor={colors.lightGray}
61 | />
62 | {
65 | forgotPasswordPress();
66 | }}
67 | fullWidth
68 | alignItems={'flex-end'}
69 | accessibilityLabel="Forgot Password Button"
70 | accessbilityRole="button"
71 | >
72 |
73 | Forgot Password?
74 |
75 |
76 | {
84 | loginPress();
85 | }}
86 | accessibilityLabel="Login Button"
87 | />
88 |
89 |
90 |
91 | or
92 |
93 |
94 | {
106 | registrationPress();
107 | }}
108 | accessibilityLabel="Create Account Button"
109 | />
110 |
111 | );
112 | };
113 |
114 | Login.defaultProps = {
115 | loginPress: console.log('implement login press'),
116 | forgotPasswordPress: console.log('implement forgot password press'),
117 | registrationPress: console.log('implement registration press'),
118 | };
119 |
--------------------------------------------------------------------------------
/template/src/components/Login/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Login';
--------------------------------------------------------------------------------
/template/src/components/Registration/Registration.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/react-native';
2 | import React from 'react';
3 |
4 | import { Registration } from './Registration';
5 |
6 | storiesOf('components/Registration', module).add('Default', () => (
7 |
8 | ));
9 |
--------------------------------------------------------------------------------
/template/src/components/Registration/Registration.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import {
3 | color,
4 | space,
5 | layout,
6 | flexbox,
7 | borders,
8 | BorderProps,
9 | ColorProps,
10 | SpaceProps,
11 | FlexProps,
12 | } from 'styled-system';
13 | import styled from '@emotion/native';
14 | import { Icon, Input } from 'react-native-elements';
15 | import { Button } from '../Button';
16 | import { Text } from '../Text';
17 | import { TextInput } from '../TextInput';
18 | import { Touchable } from '../Touchable';
19 | import { Container } from '../Container';
20 |
21 | import { colors } from '../../styles';
22 |
23 | interface RegistrationProps {
24 | /** the callbacks to be invoked onPress */
25 | createPress: () => void;
26 | goBack: () => void;
27 | }
28 |
29 | type ComponentProps = RegistrationProps & FlexProps & SpaceProps & BorderProps & ColorProps;
30 |
31 | export const Registration: FC = ({ createPress, goBack, children, ...props }) => {
32 | return (
33 |
46 |
47 | goBack()} fullWidth alignItems="flex-start">
48 |
49 |
50 |
51 |
52 |
53 |
54 | Let's create your{'\n'}account!
55 |
56 | }
59 | marginTop={2}
60 | borderRadius={5}
61 | borderColor={colors.lightGray}
62 | />
63 |
64 | }
67 | marginTop={2}
68 | borderRadius={5}
69 | borderColor={colors.lightGray}
70 | />
71 | }
74 | marginTop={2}
75 | borderRadius={5}
76 | borderColor={colors.lightGray}
77 | />
78 |
79 | {
88 | createPress();
89 | }}
90 | />
91 |
92 |
93 | );
94 | };
95 |
96 | Registration.defaultProps = {
97 | createPress: console.log('implement event'),
98 | };
99 |
--------------------------------------------------------------------------------
/template/src/components/Registration/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Registration';
--------------------------------------------------------------------------------
/template/src/components/Screen/Screen.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/react-native';
2 | import React from 'react';
3 |
4 | import { Screen } from './Screen';
5 |
6 | storiesOf('components/Screen', module)
7 | .add('Default', () => )
8 | .add('with backgroundColor', () => );
9 |
--------------------------------------------------------------------------------
/template/src/components/Screen/Screen.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactNode, FC } from 'react';
2 | import styled from '@emotion/native';
3 | import SafeAreaView from 'react-native-safe-area-view';
4 | import { color, space, FlexProps, SpaceProps } from 'styled-system';
5 |
6 | import { Container } from '../Container';
7 |
8 | import { theme } from '../../styles';
9 | import { colors } from '../../styles';
10 |
11 | const VerticallyPaddedView = styled.View`
12 | flex: 1;
13 | ${space};
14 | ${color};
15 | `;
16 |
17 | const InnerView = styled.View`
18 | flex: 1;
19 | ${space};
20 | `;
21 |
22 | interface ScreenProps {
23 | /** The content to render within the screen */
24 | children?: ReactNode;
25 | /** Whether to force the topInset. Use to prevent screen jank on tab screens */
26 | forceTopInset?: Boolean;
27 | }
28 |
29 | type ComponentProps = ScreenProps & FlexProps & SpaceProps;
30 |
31 | export const Screen: FC = ({
32 | backgroundColor,
33 | paddingTop,
34 | paddingBottom,
35 | forceTopInset,
36 | children,
37 | ...screenProps
38 | }) => (
39 |
40 |
44 |
45 | {children}
46 |
47 |
48 |
49 | );
50 |
51 | SafeAreaView.defaultProps = {
52 | bg: colors.white,
53 | };
54 |
55 | VerticallyPaddedView.defaultProps = {
56 | pt: theme.space[2],
57 | pb: theme.space[2],
58 | };
59 |
60 | InnerView.defaultProps = {};
61 |
62 | Screen.defaultProps = {};
63 |
--------------------------------------------------------------------------------
/template/src/components/Screen/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Screen';
2 |
--------------------------------------------------------------------------------
/template/src/components/Text/Text.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/react-native';
2 | import React from 'react';
3 |
4 | import { Text } from './Text';
5 |
6 | storiesOf('components/Text', module)
7 | .add('Default', () => )
8 | .add('with text prop', () => )
9 | .add('with child prop', () => Hello there );
10 |
--------------------------------------------------------------------------------
/template/src/components/Text/Text.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/native';
2 | import {
3 | color,
4 | space,
5 | typography,
6 | textStyle,
7 | ColorProps,
8 | SpaceProps,
9 | TextStyleProps,
10 | TypographyProps,
11 | } from 'styled-system';
12 |
13 | import { colors } from '../../styles';
14 |
15 | interface TextProps {}
16 |
17 | type ComponentProps = TextProps & ColorProps & SpaceProps & TextStyleProps & TypographyProps;
18 |
19 | /**
20 | * This is our primitive Text component with styled-system props applied
21 | */
22 | export const Text = styled.Text`
23 | ${space};
24 | ${color};
25 | ${typography};
26 | ${textStyle};
27 | `;
28 |
29 | Text.defaultProps = {
30 | color: colors.black,
31 | fontSize: 3,
32 | };
33 |
--------------------------------------------------------------------------------
/template/src/components/Text/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Text';
2 |
--------------------------------------------------------------------------------
/template/src/components/TextInput/TextInput.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/react-native';
2 | import React from 'react';
3 |
4 | import { TextInput } from './TextInput';
5 |
6 | storiesOf('components/TextInput', module).add('Default', () => );
7 |
--------------------------------------------------------------------------------
/template/src/components/TextInput/TextInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { TextInputProps as TextInputBaseProps } from 'react-native';
3 | import styled from '@emotion/native';
4 | import {
5 | borders,
6 | color,
7 | layout,
8 | space,
9 | flex,
10 | typography,
11 | textStyle,
12 | BorderProps,
13 | FlexProps,
14 | ColorProps,
15 | LayoutProps,
16 | SpaceProps,
17 | TextStyleProps,
18 | TypographyProps,
19 | } from 'styled-system';
20 | import { Icon } from 'react-native-elements';
21 | import { Container } from '../Container';
22 | import { Text } from '../Text';
23 | import { colors } from '../../styles';
24 |
25 | interface TextInputProps extends TextInputBaseProps {
26 | /** An optional header label to render above the input */
27 | topLabel?: string;
28 | //** An option icon to be displayed to the left of the input box */
29 | icon?: Icon;
30 | /**
31 | * Overrides the text that's read by the screen reader when the user interacts with the element. By default, the
32 | * label is constructed by traversing all the children and accumulating all the Text nodes separated by space.
33 | */
34 | accessibilityLabel?: string;
35 | }
36 |
37 | type ComponentProps = TextInputProps &
38 | ColorProps &
39 | SpaceProps &
40 | TextStyleProps &
41 | TypographyProps &
42 | BorderProps &
43 | LayoutProps &
44 | FlexProps;
45 |
46 | const InputContainer = styled(Container)``;
47 |
48 | const Input = styled.TextInput`
49 | ${flex};
50 | ${borders};
51 | ${color};
52 | ${layout};
53 | ${space};
54 | ${textStyle};
55 | ${typography};
56 | `;
57 |
58 | // NOTE: for layout and dimensioning of TextInput, wrap it in a Container
59 | export const TextInput: FC = ({
60 | topLabel,
61 | icon,
62 | accessibilityLabel,
63 | multiline,
64 | borderColor,
65 | borderRadius,
66 | ...inputProps
67 | }) => (
68 |
69 | {topLabel ? (
70 |
71 | {topLabel}
72 |
73 | ) : null}
74 |
75 | {icon ? icon : null}
76 |
84 |
85 |
86 | );
87 |
88 | InputContainer.defaultProps = {
89 | flexDirection: 'row',
90 | bg: colors.white,
91 | borderWidth: 1,
92 | borderColor: colors.black,
93 | minHeight: 40,
94 | paddingLeft: 10,
95 | alignItems: 'center',
96 | };
97 |
98 | TextInput.defaultProps = {
99 | p: 2,
100 | textAlignVertical: 'center',
101 | width: '100%',
102 | };
103 |
--------------------------------------------------------------------------------
/template/src/components/TextInput/index.ts:
--------------------------------------------------------------------------------
1 | export * from './TextInput';
--------------------------------------------------------------------------------
/template/src/components/Touchable/Touchable.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/react-native';
2 | import React from 'react';
3 |
4 | import { Touchable } from './Touchable';
5 |
6 | storiesOf('components/Touchable', module).add('Default', () => );
7 |
--------------------------------------------------------------------------------
/template/src/components/Touchable/Touchable.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/native';
2 | import {
3 | color,
4 | borders,
5 | space,
6 | layout,
7 | flexbox,
8 | BorderProps,
9 | ColorProps,
10 | SpaceProps,
11 | FlexProps,
12 | } from 'styled-system';
13 |
14 | interface TouchableProps {
15 | /** applies "flex: 1" style **/
16 | fill?: boolean;
17 | /** applies "width: 100%" style **/
18 | fullWidth?: boolean;
19 | /** centers content both vertically and horizontally **/
20 | centerContent?: boolean;
21 | }
22 |
23 | type ComponentProps = TouchableProps & BorderProps & ColorProps & FlexProps & SpaceProps;
24 |
25 | /**
26 | * This is our primitive TouchableOpacity component with styled-system props applied
27 | */
28 | export const Touchable = styled.TouchableOpacity`
29 | ${space};
30 | ${color};
31 | ${borders};
32 | ${layout};
33 | ${flexbox};
34 |
35 | ${props =>
36 | props.fill &&
37 | `
38 | flex: 1;
39 | `}
40 |
41 | ${props =>
42 | props.fullWidth &&
43 | `
44 | width: 100%;
45 | `}
46 |
47 | ${props =>
48 | props.centerContent &&
49 | `
50 | justifyContent: center;
51 | alignItems: center;
52 | `}
53 | `;
54 |
55 | Touchable.defaultProps = {};
56 |
--------------------------------------------------------------------------------
/template/src/components/Touchable/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Touchable';
2 |
--------------------------------------------------------------------------------
/template/src/navigation/AppNav.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement } from 'react';
2 | import { createStackNavigator } from '@react-navigation/stack';
3 |
4 | import { MainNav } from './MainNav';
5 |
6 | const Stack = createStackNavigator();
7 |
8 | /**
9 | * This is the main App Navigator for a logged-in user.
10 | */
11 | export const AppNav = (): ReactElement => {
12 | return (
13 |
14 |
15 | {/* If you need a top level modal, for logged in users, put it here */}
16 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/template/src/navigation/GuestNav.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement } from 'react';
2 | import { createStackNavigator } from '@react-navigation/stack';
3 |
4 | import { IntroScreen } from '../screens/IntroScreen';
5 | import { LoginScreen } from '../screens/LoginScreen';
6 | import { RegistrationScreen } from '../screens/RegistrationScreen';
7 | import { OnboardingNav } from './OnboardingNav';
8 |
9 | const Stack = createStackNavigator();
10 |
11 | /**
12 | * Guest nav typically consists of screens where the user is logged out:
13 | * Onboarding
14 | * Registration
15 | * Login
16 | * Forgot Password
17 | */
18 | export const GuestNav = (): ReactElement => {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | };
28 |
--------------------------------------------------------------------------------
/template/src/navigation/MainNav.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement } from 'react';
2 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
3 |
4 | import { createFakeScreen } from '../navigation/createFakeScreen';
5 | import { colors } from '../styles';
6 |
7 | const Tab = createBottomTabNavigator();
8 |
9 | const TabScreen1 = createFakeScreen('Tab Screen 1', colors.white);
10 | const TabScreen2 = createFakeScreen('Tab Screen 2', colors.gray);
11 | const TabScreen3 = createFakeScreen('Tab Screen 3', colors.orange);
12 |
13 | /**
14 | * Main Nav is main interface of the app, defaults to tabs.
15 | */
16 | export const MainNav = (): ReactElement => {
17 | return (
18 |
33 |
34 |
35 |
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/template/src/navigation/OnboardingNav.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement } from 'react';
2 | import { createStackNavigator } from '@react-navigation/stack';
3 |
4 | import { createFakeScreen } from '../navigation/createFakeScreen';
5 | import { colors } from '../styles';
6 |
7 | const Stack = createStackNavigator();
8 |
9 | const OnboardingScreen1 = createFakeScreen('Onboarding Screen 1', colors.blue);
10 | const OnboardingScreen2 = createFakeScreen('Onboarding Screen 2', colors.white);
11 |
12 | /**
13 | * Onboarding Nav is shown before a user registers. Typically as a swiper
14 | */
15 | export const OnboardingNav = (): ReactElement => {
16 | return (
17 |
18 |
19 |
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/template/src/navigation/createFakeScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement } from 'react';
2 |
3 | import { Screen } from '../components/Screen';
4 | import { Container } from '../components/Container';
5 | import { Text } from '../components/Text';
6 |
7 | export const createFakeScreen = (screenName, backgroundColor) => (): ReactElement => (
8 |
9 |
10 | {screenName}
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/template/src/screens/IntroScreen/IntroScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { ImageBackground, StatusBar, StyleSheet } from 'react-native';
3 | import { NavigationScreenProps } from 'react-navigation';
4 | import styled from '@emotion/native';
5 |
6 | import { Button } from '../../components/Button';
7 | import { Screen } from '../../components/Screen';
8 | import { Text } from '../../components/Text';
9 | import { Container } from '../../components/Container';
10 | import { colors } from '../../styles';
11 | import bgImage from '../../assets/images/background.png';
12 |
13 | const BackgroundImage = styled(ImageBackground)`
14 | ${StyleSheet.absoluteFillObject};
15 | `;
16 |
17 | /**
18 | * First screen a logged out user sees, welcoming them to the app.
19 | */
20 | export const IntroScreen: FC = ({ navigation }) => {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 | Welcome to the intro Screen!
28 |
29 | navigation.navigate('Login')}
35 | borderRadius={5}
36 | />
37 |
38 |
39 |
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/template/src/screens/IntroScreen/index.ts:
--------------------------------------------------------------------------------
1 | export * from './IntroScreen';
2 |
--------------------------------------------------------------------------------
/template/src/screens/LoginScreen/LoginScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { Alert, ImageBackground, StatusBar, StyleSheet } from 'react-native';
3 | import { NavigationScreenProps } from 'react-navigation';
4 | import styled from '@emotion/native';
5 |
6 | import { Login } from '../../components/Login';
7 | import { Screen } from '../../components/Screen';
8 | import bgImage from '../../assets/images/background.png';
9 | import { colors } from '../../styles';
10 |
11 | const BackgroundImage = styled(ImageBackground)`
12 | ${StyleSheet.absoluteFillObject};
13 | `;
14 | export const LoginScreen: FC = ({ navigation }) => {
15 | const loginClick = (): void => {
16 | navigation.navigate('Intro');
17 | };
18 |
19 | const registrationClick = (): void => {
20 | navigation.navigate('Registration');
21 | };
22 |
23 | const forgotPwdClick = (): void => {
24 | Alert.alert('Forgot Pwd Clicked!', 'Further implement screen');
25 | };
26 |
27 | return (
28 |
29 |
35 |
36 |
37 |
42 |
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/template/src/screens/LoginScreen/index.ts:
--------------------------------------------------------------------------------
1 | export * from './LoginScreen';
2 |
--------------------------------------------------------------------------------
/template/src/screens/RegistrationScreen/RegistrationScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { ImageBackground, StatusBar, StyleSheet } from 'react-native';
3 | import { NavigationScreenProps } from 'react-navigation';
4 | import styled from '@emotion/native';
5 |
6 | import { Registration } from '../../components/Registration';
7 | import { Screen } from '../../components/Screen';
8 |
9 | import bgImage from '../../assets/images/background.png';
10 | import { colors } from '../../styles';
11 |
12 | const BackgroundImage = styled(ImageBackground)`
13 | ${StyleSheet.absoluteFillObject};
14 | `;
15 |
16 | export const RegistrationScreen: FC = ({ navigation }) => {
17 | const createClick = () => {
18 | navigation.navigate('Intro');
19 | };
20 | return (
21 |
22 |
28 |
34 | navigation.goBack()} />
35 |
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/template/src/screens/RegistrationScreen/index.ts:
--------------------------------------------------------------------------------
1 | export * from './RegistrationScreen';
2 |
--------------------------------------------------------------------------------
/template/src/styles/colors.ts:
--------------------------------------------------------------------------------
1 | const black = '#000';
2 | const blue = '#267FA1';
3 | const bgGray = '#F9F9F9';
4 | const gray = '#C4C4C4';
5 | const grayText = '#3f3f3f';
6 | const lightGray = '#E4E4E4';
7 | const orange = '#FF8C53';
8 | const transparent = 'rgba(0, 0, 0, 0)';
9 | const white = '#fff';
10 |
11 | export const colors = {
12 | black,
13 | blue,
14 | bgGray,
15 | disabled: gray,
16 | gray,
17 | grayText,
18 | lightGray,
19 | orange,
20 | primary: blue,
21 | transparent,
22 | white,
23 | };
24 |
--------------------------------------------------------------------------------
/template/src/styles/fonts.ts:
--------------------------------------------------------------------------------
1 | const bold = {
2 | fontFamily: 'Helvetica',
3 | };
4 |
5 | const regular = {
6 | fontFamily: 'Helvetica',
7 | };
8 |
9 | const semibold = {
10 | fontFamily: 'Helvetica',
11 | };
12 |
13 | const medium = {
14 | fontFamily: 'Helvetica',
15 | };
16 |
17 | export const fonts = {
18 | regular,
19 | semibold,
20 | bold,
21 | medium,
22 | };
23 |
24 | export const fontSizes = [8, 12, 14, 16, 20, 24, 32, 48, 64, 72, 96];
25 | export const weights = [100, 200, 300, 400, 500, 600, 700, 800, 900];
26 |
--------------------------------------------------------------------------------
/template/src/styles/index.ts:
--------------------------------------------------------------------------------
1 | export { colors } from './colors';
2 | export { fonts } from './fonts';
3 | export { margins } from './margins';
4 | export { theme } from './theme';
5 |
--------------------------------------------------------------------------------
/template/src/styles/margins.ts:
--------------------------------------------------------------------------------
1 | export const margins = {
2 | screenMargin: 20
3 | };
4 |
5 | export const space = [0, 4, 8, 16, 24, 32, 64, 128];
6 | export const radii = [0, 4, 8, 14];
7 | export const radius = 14;
8 |
--------------------------------------------------------------------------------
/template/src/styles/theme.ts:
--------------------------------------------------------------------------------
1 | import { colors } from './colors';
2 | import { fonts as fontFamilies, fontSizes, weights } from './fonts';
3 | import { margins, space, radii, radius } from './margins';
4 |
5 | export const shadows = {
6 | small: `5px 5px 7px ${colors.gray}`,
7 | large: `10px 10px 10px ${colors.gray}`,
8 | };
9 |
10 | export const theme = {
11 | colors,
12 | fontFamilies,
13 | fontSizes,
14 | margins,
15 | radii,
16 | radius,
17 | shadows,
18 | space,
19 | weights,
20 | };
21 |
22 | export default theme;
23 |
--------------------------------------------------------------------------------
/template/src/types/AccessibilityRole.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Accessbility Role communicates the purpose of a component to the user of an assistive technology,
3 | * This tells a person using either VoiceOver on iOS or TalkBack on Android the type of element that is focused on.
4 | * values pulled from official react native docs
5 | * https://facebook.github.io/react-native/docs/accessibility#accessibilityrole-ios-android
6 | */
7 | export type AccessbilityRole =
8 | | 'none' /** Used when the element has no role */
9 | | 'button' /** Used when the element should be treated as a button */
10 | | 'link' /** Used when the element should be treated as a link */
11 | | 'search' /** Used when the text field element should also be treated as a search field */
12 | | 'image' /** Used when the element should be treated as an image. Can be combined with button or link, for example */
13 | | 'keyboardkey' /** Used when the element acts as a keyboard key */
14 | | 'text' /** Used when the element should be treated as static text that cannot change */
15 | | 'adjustable' /** Used when an element can be "adjusted" (e.g. a slider) */
16 | | 'imagebutton' /** Used when the element should be treated as a button and is also an image. */
17 | | 'header' /** Used when an element acts as a header for a content section (e.g. the title of a navigation bar) */
18 | | 'summary' /** Used when an element can be used to provide a quick summary of current conditions in the app when the app first launches */
19 | | 'alert' /** Used when an element contains important text to be presented to the user */
20 | | 'checkbox' /** Used when an element represents a checkbox which can be checked, unchecked, or have mixed checked state */
21 | | 'combobox' /** Used when an element represents a combo box, which allows the user to select among several choices */
22 | | 'menu' /** Used when the component is a menu of choices */
23 | | 'menubar' /** Used when a component is a container of multiple menus */
24 | | 'menuitem' /** Used to represent an item within a menu */
25 | | 'progressbar' /** Used to represent a component which indicates progress of a task */
26 | | 'radio' /** Used to represent a radio button */
27 | | 'radiogroup' /** Used to represent a group of radio buttons */
28 | | 'scrollbar' /** Used to represent a scroll bar. */
29 | | 'spinbar' /** Used to represent a button which opens a list of choices */
30 | | 'switch' /** Used to represent a switch which can be turned on and off */
31 | | 'tab' /** Used to represent a tab */
32 | | 'tablist' /** Used to represent a list of tabs */
33 | | 'timer' /** Used to represent a timer */
34 | | 'toolbar' /** Used to represent a tool bar (a container of action buttons or components) */;
35 |
--------------------------------------------------------------------------------
/template/src/utils/trimText/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './trimText';
2 |
--------------------------------------------------------------------------------
/template/src/utils/trimText/trimText.test.ts:
--------------------------------------------------------------------------------
1 | import { trimText } from './trimText';
2 |
3 | describe('trimText', () => {
4 | it('should trim a long string to the alloted length', () => {
5 | const longString = 'a'.repeat(100);
6 | const newLength = 30;
7 | const shortString = 'a'.repeat(newLength) + '...';
8 |
9 | expect(trimText(longString, newLength)).toEqual(shortString);
10 | });
11 | it('should trim a long string to the default length', () => {
12 | const longString = 'a'.repeat(100);
13 | const shortString = 'a'.repeat(70) + '...';
14 |
15 | expect(trimText(longString)).toEqual(shortString);
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/template/src/utils/trimText/trimText.ts:
--------------------------------------------------------------------------------
1 | const DEFAULT_MAX_LENGTH = 70;
2 |
3 | /**
4 | * Function to trim a string to a specific length
5 | * string - input string
6 | * length - the length to trim the string to
7 | */
8 | export function trimText(string: string, length: number = DEFAULT_MAX_LENGTH): string {
9 | return `${string.substring(0, length)}...`;
10 | }
11 |
--------------------------------------------------------------------------------
/template/storybook/index.js:
--------------------------------------------------------------------------------
1 | import StorybookUI from './storybook';
2 |
3 | export default StorybookUI;
4 |
--------------------------------------------------------------------------------
/template/storybook/rn-addons.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Currently supported RN Storybook Addons: https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md
3 | * Only those with an * are supported onDevice, and thus available in RN 4.0.4
4 | */
5 | import '@storybook/addon-ondevice-backgrounds/register';
6 |
--------------------------------------------------------------------------------
/template/storybook/stories/index.ts:
--------------------------------------------------------------------------------
1 | import '../../src/components/Button/Button.stories';
2 | import '../../src/components/Container/Container.stories';
3 | import '../../src/components/Screen/Screen.stories';
4 | import '../../src/components/Text/Text.stories';
5 | import '../../src/components/TextInput/TextInput.stories';
6 | import '../../src/components/Touchable/Touchable.stories';
7 | import '../../src/components/Login/Login.stories';
8 |
--------------------------------------------------------------------------------
/template/storybook/storybook.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AppRegistry } from 'react-native';
3 | import { addParameters, getStorybookUI, configure, addDecorator } from '@storybook/react-native';
4 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
5 |
6 | import { name as appName } from '../app.json';
7 | import { Container } from '../src/components/Container';
8 |
9 | // addons!
10 | import './rn-addons';
11 |
12 | // adding a centered-view layout!
13 | const CenterView = ({ children }) => (
14 |
15 | {children}
16 |
17 | );
18 |
19 | // global decorators!
20 | addDecorator(getStory => {getStory()} );
21 | addDecorator(withBackgrounds);
22 | addParameters({
23 | backgrounds: [
24 | { name: 'light', value: '#fff', default: true },
25 | { name: 'gray', value: '#808080' },
26 | { name: 'dark', value: '#000' },
27 | ],
28 | });
29 |
30 | // stories!
31 | configure(() => {
32 | require('./stories');
33 | }, module);
34 |
35 | // Refer to https://github.com/storybookjs/storybook/tree/master/app/react-native#start-command-parameters
36 | // To find allowed options for getStorybookUI
37 | const StorybookUIRoot = getStorybookUI({});
38 |
39 | AppRegistry.registerComponent(appName, () => StorybookUIRoot);
40 |
41 | export default StorybookUIRoot;
42 |
--------------------------------------------------------------------------------
/template/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Target latest version of ECMAScript.
4 | "target": "es2015",
5 | // Search under node_modules for non-relative imports.
6 | "moduleResolution": "node",
7 | // Process & infer types from .js files.
8 | "allowJs": true,
9 | // Don't emit; allow Babel to transform files.
10 | "noEmit": true,
11 | // Enable strictest settings like strictNullChecks & noImplicitAny.
12 | "strict": true,
13 | // Disallow features that require cross-file information for emit.
14 | "isolatedModules": true,
15 | // Import non-ES modules as default imports.
16 | "esModuleInterop": true,
17 | // Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
18 | "allowSyntheticDefaultImports": true,
19 | "jsx": "react-native",
20 | "sourceMap": true,
21 | "noImplicitReturns": true,
22 | "noImplicitAny": false,
23 | "lib": ["es7"],
24 | "plugins": [
25 | {
26 | "name": "typescript-styled-plugin"
27 | }
28 | ]
29 | },
30 | "exclude": ["node_modules"],
31 | "include": ["src", "test", "storybook"],
32 | "files": [".d.ts"]
33 | }
34 |
--------------------------------------------------------------------------------