(() => db().return().where(e.lt('rating', ratingState)).limit(frameLength).offset(frameOffset), [ frameLength, frameOffset, ratingState ]);
25 |
26 | useEffect(() => {
27 | async function mounted() {
28 | setTableLength(await fullTableSize())
29 | }
30 | mounted();
31 | }, [])
32 |
33 |
34 | const onAddPage = async () => {
35 | await db().insert({ rating: 68, "app name": "Inserted App", _position: 0 }).all();
36 | }
37 |
38 | const changePage = async (change: number) => {
39 | setFrameOffset(prev => Math.abs(prev + change));
40 | }
41 |
42 | const getFirstRecord = async () => {
43 | console.log(await db().return("app_name").where({ _position: 0 }).one());
44 | console.log(await db().return("app_name").where({ _position: 0 }).all());
45 | console.log(await db().return().where({ _position: 0 }).one());
46 | console.log(await db().return().where({ _position: 0 }).all());
47 | }
48 |
49 | return (
50 |
51 |
52 |
53 |
54 |
55 | {loading ?
: frame.map((ele: any, index: any) =>
)}
56 |
57 |
58 |
59 |
60 |
{frameOffset} - {frameOffset + Number(frameLength)} of {tableLength}
61 |
62 |
63 |
64 |
Edit db limit:
65 |
setFrameLength(+e.target.value)} value={frameLength} />
66 |
67 |
Edit Rating filter:
68 |
setRatingState(+e.target.value)} value={ratingState} />
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | )
79 | }
80 |
81 | export default UseReturnExample;
82 |
--------------------------------------------------------------------------------
/example/src/UseReturnStressTest.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useEasybase } from 'easybase-react';
3 | import CardElement from "./DbCardElement";
4 |
5 | const ChildUseReturn = () => {
6 | const [ratingState, setRatingState] = useState(50);
7 |
8 | const {
9 | db,
10 | useReturn,
11 | e
12 | } = useEasybase();
13 |
14 | const { frame, loading } = useReturn(() => db()
15 | .return()
16 | .where(e.lt('rating', ratingState))
17 | .limit(10),
18 | [ratingState]);
19 |
20 | return (
21 |
22 |
23 | {loading ?
: frame.map((ele: any, index: any) =>
)}
24 |
25 |
26 |
27 |
Edit Rating filter:
28 |
setRatingState(+e.target.value)} value={ratingState} />
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | const UseReturnStressTest = () => {
36 | const [showChild, setShowChild] = useState(true);
37 |
38 | const {
39 | db,
40 | } = useEasybase();
41 |
42 | const onAddPage = async () => {
43 | await db().insert({ rating: 68, "app name": "Inserted App", _position: 0 }).all();
44 | }
45 |
46 | return (
47 |
48 |
49 |
50 |
54 |
55 | { showChild && }
56 |
57 |
58 | )
59 | }
60 |
61 | export default UseReturnStressTest;
62 |
--------------------------------------------------------------------------------
/example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/example/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/src/styles.css:
--------------------------------------------------------------------------------
1 | .d-flex {
2 | display: flex;
3 | }
4 |
5 | .align-items-center {
6 | align-items: center;
7 | }
8 |
9 | .m-4 {
10 | margin: 10px !important;
11 | }
12 |
13 | .p-4 {
14 | padding: 10px !important;
15 | }
16 |
17 | .mp-0 {
18 | margin: 0px !important;
19 | padding: 0px !important;
20 | }
21 |
22 | .card-root {
23 | width: 200px;
24 | height: 390px;
25 | display: block;
26 | border-radius: 5px;
27 | box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
28 | margin: 30px;
29 | transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
30 | position: relative;
31 | }
32 |
33 | .card-root:hover {
34 | box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
35 | }
36 |
37 | .card-image {
38 | width: 200px;
39 | height: 200px;
40 | border-top-left-radius: 5px;
41 | border-top-right-radius: 5px;
42 | object-fit: cover;
43 | }
44 |
45 | .button-row {
46 | display: flex;
47 | width: 99vw;
48 | justify-content: space-around;
49 | }
50 |
51 | .btn {
52 | position: relative;
53 | display: block;
54 | margin: 30px auto;
55 | padding: 0;
56 | overflow: hidden;
57 | border-width: 0;
58 | outline: none;
59 | border-radius: 2px;
60 | box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
61 | background-color: #2ecc71;
62 | color: #ecf0f1;
63 | transition: background-color .3s;
64 | }
65 |
66 | .btn:hover, .btn:focus {
67 | background-color: #27ae60;
68 | }
69 |
70 | .btn>* {
71 | position: relative;
72 | }
73 |
74 | .btn span {
75 | display: block;
76 | padding: 12px 24px;
77 | pointer-events: auto;
78 | cursor: pointer;
79 | }
80 |
81 | .btn:active:before {
82 | width: 120%;
83 | padding-top: 120%;
84 | transition: width .2s ease-out, padding-top .2s ease-out;
85 | }
86 |
87 | .btn.orange {
88 | background-color: #e67e22;
89 | }
90 |
91 | .btn.orange:hover, .btn.orange:focus {
92 | background-color: #d35400;
93 | }
94 |
95 | .btn.green {
96 | background-color: #66bd0e;
97 | }
98 |
99 | .btn.green:hover, .btn.green:focus {
100 | background-color: #08c908;
101 | }
102 |
103 | .card-delete-button::before {
104 | position: absolute;
105 | width: 10px;
106 | top: 2px;
107 | left: 28%;
108 | content: "X";
109 | font-size: 13px;
110 | color: white;
111 | }
112 |
113 | .card-delete-button {
114 | border-radius: 50%;
115 | position: absolute;
116 | top: -7px;
117 | right: -7px;
118 | width: 20px;
119 | height: 20px;
120 | background-color: red;
121 | z-index: 10;
122 | box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
123 | cursor: pointer;
124 | }
125 |
126 | .spacer {
127 | height: 60px;
128 | width: 0.125rem;
129 | border-radius: 3px;
130 | background-color: #a7a7a7;
131 | }
132 |
133 | .loader {
134 | border: 16px solid #f3f3f3;
135 | border-radius: 50%;
136 | border-top: 16px solid #3498db;
137 | width: 120px;
138 | height: 120px;
139 | -webkit-animation: spin 2s linear infinite;
140 | /* Safari */
141 | animation: spin 2s linear infinite;
142 | }
143 |
144 | /* Safari */
145 |
146 | @-webkit-keyframes spin {
147 | 0% {
148 | -webkit-transform: rotate(0deg);
149 | }
150 | 100% {
151 | -webkit-transform: rotate(360deg);
152 | }
153 | }
154 |
155 | @keyframes spin {
156 | 0% {
157 | transform: rotate(0deg);
158 | }
159 | 100% {
160 | transform: rotate(360deg);
161 | }
162 | }
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "module": "esnext",
5 | "lib": [
6 | "dom",
7 | "esnext"
8 | ],
9 | "moduleResolution": "node",
10 | "jsx": "react-jsx",
11 | "sourceMap": true,
12 | "declaration": true,
13 | "esModuleInterop": true,
14 | "noImplicitReturns": true,
15 | "noImplicitThis": true,
16 | "noImplicitAny": true,
17 | "strictNullChecks": true,
18 | "suppressImplicitAnyIndexErrors": true,
19 | "noUnusedLocals": false,
20 | "noUnusedParameters": true,
21 | "allowSyntheticDefaultImports": true,
22 | "target": "es5",
23 | "allowJs": true,
24 | "skipLibCheck": true,
25 | "strict": true,
26 | "forceConsistentCasingInFileNames": true,
27 | "resolveJsonModule": true,
28 | "isolatedModules": true,
29 | "noEmit": true,
30 | "noFallthroughCasesInSwitch": true
31 | },
32 | "include": [
33 | "src"
34 | ],
35 | "exclude": [
36 | "node_modules",
37 | "build"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/native-example/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/native-example/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 |
--------------------------------------------------------------------------------
/native-example/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 |
34 | # node.js
35 | #
36 | node_modules/
37 | npm-debug.log
38 | yarn-error.log
39 |
40 | # BUCK
41 | buck-out/
42 | \.buckd/
43 | *.keystore
44 | !debug.keystore
45 |
46 | # Bundle artifacts
47 | *.jsbundle
48 |
49 | # CocoaPods
50 | /ios/Pods/
51 |
52 | # Expo
53 | .expo/
54 | web-build/
55 |
--------------------------------------------------------------------------------
/native-example/App.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | /* eslint-disable operator-linebreak */
3 | import React from 'react';
4 | import { StyleSheet, Text, View, Button } from 'react-native';
5 | import { useEasybase, EasybaseProvider } from 'easybase-react';
6 | import { NativeAuth } from 'easybase-react/native';
7 | import ebconfig from './ebconfig';
8 |
9 | const SignOutButton = () =>
10 |
11 | function Inner() {
12 | const { useReturn, db } = useEasybase();
13 | const { frame } = useReturn(() => db('REACT TEST', true).return().limit(10))
14 |
15 | return (
16 |
17 | { frame.map(e => {JSON.stringify(e)}) }
18 |
19 | )
20 | }
21 |
22 | export default function app() {
23 | return (
24 |
25 |
26 |
27 | You're in
28 |
29 |
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | const styles = StyleSheet.create({
37 | container: {
38 | flex: 1,
39 | backgroundColor: '#fff',
40 | alignItems: 'center',
41 | justifyContent: 'center'
42 | },
43 | accountInput: {
44 | height: 40,
45 | borderColor: 'gray',
46 | borderWidth: 1,
47 | width: "75%",
48 | margin: 10,
49 | fontSize: 22,
50 | textAlign: "center"
51 | },
52 | title: {
53 | fontSize: 30,
54 | fontWeight: "500",
55 | fontStyle: "italic",
56 | marginBottom: 30
57 | }
58 | });
59 |
--------------------------------------------------------------------------------
/native-example/android/app/BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.nativeexample",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.nativeexample",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/native-example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation. If none specified and
19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
20 | * // default. Can be overridden with ENTRY_FILE environment variable.
21 | * entryFile: "index.android.js",
22 | *
23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format
24 | * bundleCommand: "ram-bundle",
25 | *
26 | * // whether to bundle JS and assets in debug mode
27 | * bundleInDebug: false,
28 | *
29 | * // whether to bundle JS and assets in release mode
30 | * bundleInRelease: true,
31 | *
32 | * // whether to bundle JS and assets in another build variant (if configured).
33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
34 | * // The configuration property can be in the following formats
35 | * // 'bundleIn${productFlavor}${buildType}'
36 | * // 'bundleIn${buildType}'
37 | * // bundleInFreeDebug: true,
38 | * // bundleInPaidRelease: true,
39 | * // bundleInBeta: true,
40 | *
41 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
42 | * // for example: to disable dev mode in the staging build type (if configured)
43 | * devDisabledInStaging: true,
44 | * // The configuration property can be in the following formats
45 | * // 'devDisabledIn${productFlavor}${buildType}'
46 | * // 'devDisabledIn${buildType}'
47 | *
48 | * // the root of your project, i.e. where "package.json" lives
49 | * root: "../../",
50 | *
51 | * // where to put the JS bundle asset in debug mode
52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
53 | *
54 | * // where to put the JS bundle asset in release mode
55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
56 | *
57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
58 | * // require('./image.png')), in debug mode
59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
60 | *
61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
62 | * // require('./image.png')), in release mode
63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
64 | *
65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
69 | * // for example, you might want to remove it from here.
70 | * inputExcludes: ["android/**", "ios/**"],
71 | *
72 | * // override which node gets called and with what additional arguments
73 | * nodeExecutableAndArgs: ["node"],
74 | *
75 | * // supply additional arguments to the packager
76 | * extraPackagerArgs: []
77 | * ]
78 | */
79 |
80 | project.ext.react = [
81 | enableHermes: false
82 | ]
83 |
84 | apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
85 | apply from: "../../node_modules/react-native/react.gradle"
86 | apply from: "../../node_modules/expo-constants/scripts/get-app-config-android.gradle"
87 | apply from: "../../node_modules/expo-updates/scripts/create-manifest-android.gradle"
88 |
89 | /**
90 | * Set this to true to create two separate APKs instead of one:
91 | * - An APK that only works on ARM devices
92 | * - An APK that only works on x86 devices
93 | * The advantage is the size of the APK is reduced by about 4MB.
94 | * Upload all the APKs to the Play Store and people will download
95 | * the correct one based on the CPU architecture of their device.
96 | */
97 | def enableSeparateBuildPerCPUArchitecture = false
98 |
99 | /**
100 | * Run Proguard to shrink the Java bytecode in release builds.
101 | */
102 | def enableProguardInReleaseBuilds = false
103 |
104 | /**
105 | * The preferred build flavor of JavaScriptCore.
106 | *
107 | * For example, to use the international variant, you can use:
108 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
109 | *
110 | * The international variant includes ICU i18n library and necessary data
111 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
112 | * give correct results when using with locales other than en-US. Note that
113 | * this variant is about 6MiB larger per architecture than default.
114 | */
115 | def jscFlavor = 'org.webkit:android-jsc:+'
116 |
117 | /**
118 | * Whether to enable the Hermes VM.
119 | *
120 | * This should be set on project.ext.react and mirrored here. If it is not set
121 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
122 | * and the benefits of using Hermes will therefore be sharply reduced.
123 | */
124 | def enableHermes = project.ext.react.get("enableHermes", false);
125 |
126 | android {
127 | compileSdkVersion rootProject.ext.compileSdkVersion
128 |
129 | compileOptions {
130 | sourceCompatibility JavaVersion.VERSION_1_8
131 | targetCompatibility JavaVersion.VERSION_1_8
132 | }
133 |
134 | defaultConfig {
135 | applicationId "com.nativeexample"
136 | minSdkVersion rootProject.ext.minSdkVersion
137 | targetSdkVersion rootProject.ext.targetSdkVersion
138 | versionCode 1
139 | versionName "1.0"
140 | }
141 | splits {
142 | abi {
143 | reset()
144 | enable enableSeparateBuildPerCPUArchitecture
145 | universalApk false // If true, also generate a universal APK
146 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
147 | }
148 | }
149 | signingConfigs {
150 | debug {
151 | storeFile file('debug.keystore')
152 | storePassword 'android'
153 | keyAlias 'androiddebugkey'
154 | keyPassword 'android'
155 | }
156 | }
157 | buildTypes {
158 | debug {
159 | signingConfig signingConfigs.debug
160 | }
161 | release {
162 | // Caution! In production, you need to generate your own keystore file.
163 | // see https://reactnative.dev/docs/signed-apk-android.
164 | signingConfig signingConfigs.debug
165 | minifyEnabled enableProguardInReleaseBuilds
166 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
167 | }
168 | }
169 |
170 | // applicationVariants are e.g. debug, release
171 | applicationVariants.all { variant ->
172 | variant.outputs.each { output ->
173 | // For each separate APK per architecture, set a unique version code as described here:
174 | // https://developer.android.com/studio/build/configure-apk-splits.html
175 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
176 | def abi = output.getFilter(OutputFile.ABI)
177 | if (abi != null) { // null for the universal-debug, universal-release variants
178 | output.versionCodeOverride =
179 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
180 | }
181 |
182 | }
183 | }
184 | }
185 |
186 | dependencies {
187 | implementation fileTree(dir: "libs", include: ["*.jar"])
188 | //noinspection GradleDynamicVersion
189 | implementation "com.facebook.react:react-native:+" // From node_modules
190 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
191 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
192 | exclude group:'com.facebook.fbjni'
193 | }
194 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
195 | exclude group:'com.facebook.flipper'
196 | exclude group:'com.squareup.okhttp3', module:'okhttp'
197 | }
198 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
199 | exclude group:'com.facebook.flipper'
200 | }
201 | addUnimodulesDependencies()
202 |
203 | if (enableHermes) {
204 | def hermesPath = "../../node_modules/hermes-engine/android/";
205 | debugImplementation files(hermesPath + "hermes-debug.aar")
206 | releaseImplementation files(hermesPath + "hermes-release.aar")
207 | } else {
208 | implementation jscFlavor
209 | }
210 | }
211 |
212 | // Run this once to be able to run the application with BUCK
213 | // puts all compile dependencies into folder libs for BUCK to use
214 | task copyDownloadableDepsToLibs(type: Copy) {
215 | from configurations.compile
216 | into 'libs'
217 | }
218 |
219 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
220 |
--------------------------------------------------------------------------------
/native-example/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/native-example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/debug.keystore
--------------------------------------------------------------------------------
/native-example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/native-example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/native-example/android/app/src/debug/java/com/nativeexample/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.nativeexample;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | public class ReactNativeFlipper {
28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
29 | if (FlipperUtils.shouldEnableFlipper(context)) {
30 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
31 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
32 | client.addPlugin(new ReactFlipperPlugin());
33 | client.addPlugin(new DatabasesFlipperPlugin(context));
34 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
35 | client.addPlugin(CrashReporterPlugin.getInstance());
36 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
37 | NetworkingModule.setCustomClientBuilder(
38 | new NetworkingModule.CustomClientBuilder() {
39 | @Override
40 | public void apply(OkHttpClient.Builder builder) {
41 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
42 | }
43 | });
44 | client.addPlugin(networkFlipperPlugin);
45 | client.start();
46 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
47 | // Hence we run if after all native modules have been initialized
48 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
49 | if (reactContext == null) {
50 | reactInstanceManager.addReactInstanceEventListener(
51 | new ReactInstanceManager.ReactInstanceEventListener() {
52 | @Override
53 | public void onReactContextInitialized(ReactContext reactContext) {
54 | reactInstanceManager.removeReactInstanceEventListener(this);
55 | reactContext.runOnNativeModulesQueueThread(
56 | new Runnable() {
57 | @Override
58 | public void run() {
59 | client.addPlugin(new FrescoFlipperPlugin());
60 | }
61 | });
62 | }
63 | });
64 | } else {
65 | client.addPlugin(new FrescoFlipperPlugin());
66 | }
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/native-example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/native-example/android/app/src/main/java/com/nativeexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.nativeexample;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.facebook.react.ReactActivity;
6 | import com.facebook.react.ReactActivityDelegate;
7 | import com.facebook.react.ReactRootView;
8 | import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
9 |
10 | import expo.modules.splashscreen.singletons.SplashScreen;
11 | import expo.modules.splashscreen.SplashScreenImageResizeMode;
12 |
13 | public class MainActivity extends ReactActivity {
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(null);
17 | // SplashScreen.show(...) has to be called after super.onCreate(...)
18 | // Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually
19 | SplashScreen.show(this, SplashScreenImageResizeMode.CONTAIN, ReactRootView.class, false);
20 | }
21 |
22 |
23 | /**
24 | * Returns the name of the main component registered from JavaScript.
25 | * This is used to schedule rendering of the component.
26 | */
27 | @Override
28 | protected String getMainComponentName() {
29 | return "main";
30 | }
31 |
32 | @Override
33 | protected ReactActivityDelegate createReactActivityDelegate() {
34 | return new ReactActivityDelegate(this, getMainComponentName()) {
35 | @Override
36 | protected ReactRootView createRootView() {
37 | return new RNGestureHandlerEnabledRootView(MainActivity.this);
38 | }
39 | };
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/native-example/android/app/src/main/java/com/nativeexample/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.nativeexample;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.net.Uri;
6 |
7 | import com.facebook.react.PackageList;
8 | import com.facebook.react.ReactApplication;
9 | import com.facebook.react.ReactInstanceManager;
10 | import com.facebook.react.ReactNativeHost;
11 | import com.facebook.react.ReactPackage;
12 | import com.facebook.react.shell.MainReactPackage;
13 | import com.facebook.soloader.SoLoader;
14 | import com.nativeexample.generated.BasePackageList;
15 |
16 | import org.unimodules.adapters.react.ReactAdapterPackage;
17 | import org.unimodules.adapters.react.ModuleRegistryAdapter;
18 | import org.unimodules.adapters.react.ReactModuleRegistryProvider;
19 | import org.unimodules.core.interfaces.Package;
20 | import org.unimodules.core.interfaces.SingletonModule;
21 | import expo.modules.constants.ConstantsPackage;
22 | import expo.modules.permissions.PermissionsPackage;
23 | import expo.modules.filesystem.FileSystemPackage;
24 | import expo.modules.updates.UpdatesController;
25 |
26 | import com.facebook.react.bridge.JSIModulePackage;
27 | import com.swmansion.reanimated.ReanimatedJSIModulePackage;
28 |
29 | import java.lang.reflect.InvocationTargetException;
30 | import java.util.Arrays;
31 | import java.util.List;
32 | import javax.annotation.Nullable;
33 |
34 | public class MainApplication extends Application implements ReactApplication {
35 | private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
36 | new BasePackageList().getPackageList()
37 | );
38 |
39 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
40 | @Override
41 | public boolean getUseDeveloperSupport() {
42 | return BuildConfig.DEBUG;
43 | }
44 |
45 | @Override
46 | protected List getPackages() {
47 | List packages = new PackageList(this).getPackages();
48 | packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider));
49 | return packages;
50 | }
51 |
52 | @Override
53 | protected String getJSMainModuleName() {
54 | return "index";
55 | }
56 |
57 | @Override
58 | protected JSIModulePackage getJSIModulePackage() {
59 | return new ReanimatedJSIModulePackage();
60 | }
61 |
62 | @Override
63 | protected @Nullable String getJSBundleFile() {
64 | if (BuildConfig.DEBUG) {
65 | return super.getJSBundleFile();
66 | } else {
67 | return UpdatesController.getInstance().getLaunchAssetFile();
68 | }
69 | }
70 |
71 | @Override
72 | protected @Nullable String getBundleAssetName() {
73 | if (BuildConfig.DEBUG) {
74 | return super.getBundleAssetName();
75 | } else {
76 | return UpdatesController.getInstance().getBundleAssetName();
77 | }
78 | }
79 | };
80 |
81 | @Override
82 | public ReactNativeHost getReactNativeHost() {
83 | return mReactNativeHost;
84 | }
85 |
86 | @Override
87 | public void onCreate() {
88 | super.onCreate();
89 | SoLoader.init(this, /* native exopackage */ false);
90 |
91 | if (!BuildConfig.DEBUG) {
92 | UpdatesController.initialize(this);
93 | }
94 |
95 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
96 | }
97 |
98 | /**
99 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
100 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
101 | *
102 | * @param context
103 | * @param reactInstanceManager
104 | */
105 | private static void initializeFlipper(
106 | Context context, ReactInstanceManager reactInstanceManager) {
107 | if (BuildConfig.DEBUG) {
108 | try {
109 | /*
110 | We use reflection here to pick up the class that initializes Flipper,
111 | since Flipper library is not available in release mode
112 | */
113 | Class> aClass = Class.forName("com.nativeexample.ReactNativeFlipper");
114 | aClass
115 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
116 | .invoke(null, context, reactInstanceManager);
117 | } catch (ClassNotFoundException e) {
118 | e.printStackTrace();
119 | } catch (NoSuchMethodException e) {
120 | e.printStackTrace();
121 | } catch (IllegalAccessException e) {
122 | e.printStackTrace();
123 | } catch (InvocationTargetException e) {
124 | e.printStackTrace();
125 | }
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/drawable/splashscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/drawable/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/drawable/splashscreen_image.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #FFFFFF
5 |
6 |
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | native-example
3 |
4 |
--------------------------------------------------------------------------------
/native-example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/native-example/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.3"
6 | minSdkVersion = 21
7 | compileSdkVersion = 30
8 | targetSdkVersion = 30
9 | }
10 | repositories {
11 | google()
12 | jcenter()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle:4.1.0")
16 |
17 | // NOTE: Do not place your application dependencies here; they belong
18 | // in the individual module build.gradle files
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | mavenLocal()
25 | maven {
26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
27 | url("$rootDir/../node_modules/react-native/android")
28 | }
29 | maven {
30 | // Android JSC is installed from npm
31 | url("$rootDir/../node_modules/jsc-android/dist")
32 | }
33 |
34 | google()
35 | jcenter()
36 | maven { url 'https://www.jitpack.io' }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/native-example/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 |
25 | # Automatically convert third-party libraries to use AndroidX
26 | android.enableJetifier=true
27 |
28 | # Version of flipper SDK to use with React Native
29 | FLIPPER_VERSION=0.54.0
--------------------------------------------------------------------------------
/native-example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/native-example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/native-example/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 |
--------------------------------------------------------------------------------
/native-example/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 |
--------------------------------------------------------------------------------
/native-example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'nativeexample'
2 |
3 | apply from: '../node_modules/react-native-unimodules/gradle.groovy'
4 | includeUnimodulesProjects()
5 |
6 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
7 | applyNativeModulesSettingsGradle(settings)
8 |
9 | include ':app'
10 |
--------------------------------------------------------------------------------
/native-example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "native-example",
3 | "displayName": "native-example",
4 | "expo": {
5 | "name": "native-example",
6 | "slug": "native-example",
7 | "version": "1.0.0",
8 | "assetBundlePatterns": [
9 | "**/*"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/native-example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | plugins: ['react-native-reanimated/plugin']
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/native-example/index.js:
--------------------------------------------------------------------------------
1 | import 'react-native-gesture-handler';
2 | import { registerRootComponent } from 'expo';
3 |
4 | import App from './App';
5 |
6 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
7 | // It also ensures that whether you load the app in Expo Go or in a native build,
8 | // the environment is set up appropriately
9 | registerRootComponent(App);
10 |
--------------------------------------------------------------------------------
/native-example/ios/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../node_modules/react-native/scripts/react_native_pods'
2 | require_relative '../node_modules/react-native-unimodules/cocoapods.rb'
3 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
4 |
5 | platform :ios, '11.0'
6 |
7 | target 'nativeexample' do
8 | use_unimodules!
9 | config = use_native_modules!
10 |
11 | use_react_native!(:path => config["reactNativePath"])
12 |
13 | # Uncomment to opt-in to using Flipper
14 | #
15 | # if !ENV['CI']
16 | # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1')
17 | # post_install do |installer|
18 | # flipper_post_install(installer)
19 | # end
20 | # end
21 | end
22 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample.xcodeproj/xcshareddata/xcschemes/nativeexample.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 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 |
6 | #import
7 |
8 | @interface AppDelegate : UMAppDelegateWrapper
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import
6 | #import
7 |
8 | #import
9 | #import
10 | #import
11 | #import
12 | #import
13 |
14 | #if defined(FB_SONARKIT_ENABLED) && __has_include()
15 | #import
16 | #import
17 | #import
18 | #import
19 | #import
20 | #import
21 |
22 | static void InitializeFlipper(UIApplication *application) {
23 | FlipperClient *client = [FlipperClient sharedClient];
24 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
25 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
26 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
27 | [client addPlugin:[FlipperKitReactPlugin new]];
28 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
29 | [client start];
30 | }
31 | #endif
32 |
33 | @interface AppDelegate ()
34 |
35 | @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter;
36 | @property (nonatomic, strong) NSDictionary *launchOptions;
37 |
38 | @end
39 |
40 | @implementation AppDelegate
41 |
42 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
43 | {
44 | #if defined(FB_SONARKIT_ENABLED) && __has_include()
45 | InitializeFlipper(application);
46 | #endif
47 |
48 | self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]];
49 | self.launchOptions = launchOptions;
50 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
51 | #ifdef DEBUG
52 | [self initializeReactNativeApp];
53 | #else
54 | EXUpdatesAppController *controller = [EXUpdatesAppController sharedInstance];
55 | controller.delegate = self;
56 | [controller startAndShowLaunchScreen:self.window];
57 | #endif
58 |
59 | [super application:application didFinishLaunchingWithOptions:launchOptions];
60 |
61 | return YES;
62 | }
63 |
64 | - (RCTBridge *)initializeReactNativeApp
65 | {
66 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:self.launchOptions];
67 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
68 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
69 |
70 | UIViewController *rootViewController = [UIViewController new];
71 | rootViewController.view = rootView;
72 | self.window.rootViewController = rootViewController;
73 | [self.window makeKeyAndVisible];
74 |
75 | return bridge;
76 | }
77 |
78 | - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge
79 | {
80 | NSArray> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge];
81 | // If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here!
82 | return extraModules;
83 | }
84 |
85 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
86 | #ifdef DEBUG
87 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
88 | #else
89 | return [[EXUpdatesAppController sharedInstance] launchAssetUrl];
90 | #endif
91 | }
92 |
93 | - (void)appController:(EXUpdatesAppController *)appController didStartWithSuccess:(BOOL)success {
94 | appController.bridge = [self initializeReactNativeApp];
95 | EXSplashScreenService *splashScreenService = (EXSplashScreenService *)[UMModuleRegistryProvider getSingletonModuleForClass:[EXSplashScreenService class]];
96 | [splashScreenService showSplashScreenFor:self.window.rootViewController];
97 | }
98 |
99 | // Linking API
100 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
101 | return [RCTLinkingManager application:application openURL:url options:options];
102 | }
103 |
104 | // Universal Links
105 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler {
106 | return [RCTLinkingManager application:application
107 | continueUserActivity:userActivity
108 | restorationHandler:restorationHandler];
109 | }
110 |
111 | @end
112 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/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" : "expo"
37 | }
38 | }
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "expo"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Images.xcassets/SplashScreen.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "splashscreen.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Images.xcassets/SplashScreen.imageset/splashscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/ios/nativeexample/Images.xcassets/SplashScreen.imageset/splashscreen.png
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Images.xcassets/SplashScreenBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "background.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Images.xcassets/SplashScreenBackground.imageset/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybase/easybase-react/7d9276b7a991842b1239fcbb48db23e9ceeee0ee/native-example/ios/nativeexample/Images.xcassets/SplashScreenBackground.imageset/background.png
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleSignature
18 | ????
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | UILaunchStoryboardName
39 | SplashScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | armv7
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 | UIStatusBarStyle
53 | UIStatusBarStyleDefault
54 |
55 |
56 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/SplashScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
39 |
40 |
41 |
42 |
53 |
54 |
55 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/Supporting/Expo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EXUpdatesSDKVersion
6 | YOUR-APP-SDK-VERSION-HERE
7 | EXUpdatesURL
8 | YOUR-APP-URL-HERE
9 |
10 |
11 |
--------------------------------------------------------------------------------
/native-example/ios/nativeexample/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 |
11 |
--------------------------------------------------------------------------------
/native-example/metro.config.js:
--------------------------------------------------------------------------------
1 | // Learn more https://docs.expo.io/guides/customizing-metro
2 | const { getDefaultConfig } = require('expo/metro-config');
3 |
4 | module.exports = getDefaultConfig(__dirname);
5 |
--------------------------------------------------------------------------------
/native-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "index.js",
3 | "scripts": {
4 | "android": "npm run reinit && react-native run-android",
5 | "ios": "npm run reinit && react-native run-ios",
6 | "web": "npm run reinit && expo start --web",
7 | "start": "npm run reinit && react-native start",
8 | "resetCache": "react-native start --reset-cache",
9 | "reinit": "npm i github:easybase/easybase-react#dev"
10 | },
11 | "dependencies": {
12 | "easybase-react": "github:easybase/easybase-react#dev",
13 | "expo": "~41.0.1",
14 | "expo-splash-screen": "~0.10.2",
15 | "expo-status-bar": "~1.0.4",
16 | "expo-updates": "~0.5.4",
17 | "react": "16.13.1",
18 | "react-dom": "16.13.1",
19 | "react-native": "~0.63.4",
20 | "react-native-gesture-handler": "~1.10.2",
21 | "react-native-reanimated": "~2.1.0",
22 | "react-native-screens": "~3.0.0",
23 | "react-native-unimodules": "~0.13.3",
24 | "react-native-web": "~0.13.12"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.9.0"
28 | },
29 | "private": true
30 | }
31 |
--------------------------------------------------------------------------------
/native/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "easybase-react/native",
3 | "private": true,
4 | "main": "../dist/ReactNative.js",
5 | "module": "../dist/ReactNative.modern.js",
6 | "types": "../dist/ui/ReactNative.d.ts"
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "easybase-react",
3 | "version": "2.2.13",
4 | "description": "Serverless functionality for React and React Native with Easybase",
5 | "author": "easybase",
6 | "license": "MIT",
7 | "repository": "easybase/easybase-react",
8 | "main": "dist/index.js",
9 | "module": "dist/index.modern.js",
10 | "source": "src/index.tsx",
11 | "engines": {
12 | "node": ">=10"
13 | },
14 | "scripts": {
15 | "build": "npm run clean && npm run microbundle && npm run fixDist",
16 | "prepare": "npm run clean && npm run microbundle && npm run fixDist",
17 | "microbundle": "microbundle build --entry src/index.tsx --entry src/ui/ReactNative.tsx --entry src/cache.native.ts --entry src/cache.ts --jsx React.createElement --external react,react-native --no-compress --format modern,cjs",
18 | "genDocs": "typedoc",
19 | "clean": "shx rm -rf dist",
20 | "fixDist": "shx cp -r dist/src/ui/utils.d.ts dist/ui/utils.d.ts && shx cp -r dist/src/ui/uiTypes.d.ts dist/ui/uiTypes.d.ts && shx cp -r dist/src/ui/Auth/. dist/ui/Auth && shx rm -rf dist/src/ && shx rm -rf dist/src/ ",
21 | "test": "echo 'no test specified' || true"
22 | },
23 | "peerDependencies": {
24 | "react": ">= 16.8.0"
25 | },
26 | "devDependencies": {
27 | "@types/node": "^12.12.38",
28 | "@types/react": "17.0.1",
29 | "@types/react-dom": "17.0.1",
30 | "@types/styled-components": "^5.1.9",
31 | "@types/styled-components-react-native": "^5.1.1",
32 | "@typescript-eslint/eslint-plugin": "^4.26.0",
33 | "@typescript-eslint/parser": "^4.26.0",
34 | "cross-env": "^7.0.2",
35 | "eslint": "^7.27.0",
36 | "eslint-config-standard": "^16.0.3",
37 | "eslint-config-standard-react": "^11.0.1",
38 | "eslint-plugin-import": "^2.23.4",
39 | "eslint-plugin-node": "^11.1.0",
40 | "eslint-plugin-promise": "^5.1.0",
41 | "eslint-plugin-react": "^7.24.0",
42 | "eslint-plugin-react-hooks": "^4.2.0",
43 | "microbundle": "^0.13.1",
44 | "react": "17.0.1",
45 | "react-dom": "17.0.1",
46 | "react-scripts": "^4.0.3",
47 | "shx": "^0.3.3",
48 | "typedoc": "^0.20.36",
49 | "typedoc-plugin-markdown": "3.5.0",
50 | "typescript": "^4.2.4"
51 | },
52 | "files": [
53 | "dist",
54 | "scripts",
55 | "native"
56 | ],
57 | "dependencies": {
58 | "cross-fetch": "^3.1.4",
59 | "easybasejs": "4.2.22",
60 | "easyqb": "^1.0.20",
61 | "fast-deep-equal": "^3.1.3",
62 | "ga-4-react": "^0.1.281",
63 | "object-observer": "^4.0.3",
64 | "react-hook-form": "^7.7.1",
65 | "react-hot-toast": "^2.0.0",
66 | "styled-components": "^5.3.0"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/EasybaseContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 | import {
3 | ContextValue,
4 | UseReturnValue,
5 | ConfigureFrameOptions,
6 | StatusResponse,
7 | AddRecordOptions,
8 | UpdateRecordAttachmentOptions,
9 | FrameConfiguration,
10 | QueryOptions,
11 | EmailTemplate,
12 | FileFromURI
13 | } from "./types/types";
14 | import { SQW } from "easyqb/types/sq";
15 | import { NewExpression } from "easyqb/types/expression";
16 |
17 | function Frame(): Record[];
18 | function Frame(index: number): Record;
19 | function Frame(index?: number): Record[] | Record {
20 | return [];
21 | }
22 |
23 | const c: ContextValue = {
24 | configureFrame: (_: ConfigureFrameOptions) => ({}) as StatusResponse,
25 | addRecord: async (_: AddRecordOptions) => ({}) as StatusResponse,
26 | deleteRecord: async (_: Record) => ({}) as StatusResponse,
27 | sync: async () => ({}) as StatusResponse,
28 | updateRecordImage: async (_: UpdateRecordAttachmentOptions) => ({}) as StatusResponse,
29 | updateRecordVideo: async (_: UpdateRecordAttachmentOptions) => ({}) as StatusResponse,
30 | updateRecordFile: async (_: UpdateRecordAttachmentOptions) => ({}) as StatusResponse,
31 | Frame,
32 | useFrameEffect: (_: React.EffectCallback) => {},
33 | fullTableSize: async () => 0,
34 | tableTypes: async() => ({}) as Record,
35 | currentConfiguration: () => ({}) as FrameConfiguration,
36 | Query: async (_: QueryOptions) => ([]),
37 | getUserAttributes: async() => ({}) as Record,
38 | isUserSignedIn: () => false,
39 | setUserAttribute: async (_: string, _2: string) => ({}) as StatusResponse,
40 | resetUserPassword: async (_: string, _2: string) => ({}) as StatusResponse,
41 | signIn: async (_: string, _2: string) => ({}) as StatusResponse,
42 | signOut: () => {},
43 | signUp: async (_: string, _2: string, _3?: Record) => ({}) as StatusResponse,
44 | onSignIn: (_: () => void) => {},
45 | db: (_?: string) => ({}) as SQW,
46 | dbEventListener: (_: () => void) => () => {},
47 | useReturn: (_: () => SQW) => ({}) as UseReturnValue,
48 | e: ({}) as NewExpression,
49 | setFile: async (_: string, _2: string, _3: File | FileFromURI, _4?: string) => ({}) as StatusResponse,
50 | setImage: async (_: string, _2: string, _3: File | FileFromURI, _4?: string) => ({}) as StatusResponse,
51 | setVideo: async (_: string, _2: string, _3: File | FileFromURI, _4?: string) => ({}) as StatusResponse,
52 | forgotPassword: async (_: string, _2?: EmailTemplate) => ({}) as StatusResponse,
53 | forgotPasswordConfirm: async (_: string, _2: string, _3: string) => ({}) as StatusResponse,
54 | userID: () => undefined
55 | }
56 |
57 | export default createContext(c);
58 |
--------------------------------------------------------------------------------
/src/assets/image-extensions.json:
--------------------------------------------------------------------------------
1 | [
2 | "ase",
3 | "art",
4 | "bmp",
5 | "blp",
6 | "cd5",
7 | "cit",
8 | "cpt",
9 | "cr2",
10 | "cut",
11 | "dds",
12 | "dib",
13 | "djvu",
14 | "egt",
15 | "exif",
16 | "gif",
17 | "gpl",
18 | "grf",
19 | "icns",
20 | "ico",
21 | "iff",
22 | "jng",
23 | "jpeg",
24 | "jpg",
25 | "jfif",
26 | "jp2",
27 | "jps",
28 | "lbm",
29 | "max",
30 | "miff",
31 | "mng",
32 | "msp",
33 | "nitf",
34 | "ota",
35 | "pbm",
36 | "pc1",
37 | "pc2",
38 | "pc3",
39 | "pcf",
40 | "pcx",
41 | "pdn",
42 | "pgm",
43 | "PI1",
44 | "PI2",
45 | "PI3",
46 | "pict",
47 | "pct",
48 | "pnm",
49 | "pns",
50 | "ppm",
51 | "psb",
52 | "psd",
53 | "pdd",
54 | "psp",
55 | "px",
56 | "pxm",
57 | "pxr",
58 | "qfx",
59 | "raw",
60 | "rle",
61 | "sct",
62 | "sgi",
63 | "rgb",
64 | "int",
65 | "bw",
66 | "tga",
67 | "tiff",
68 | "tif",
69 | "vtf",
70 | "xbm",
71 | "xcf",
72 | "xpm",
73 | "3dv",
74 | "amf",
75 | "ai",
76 | "awg",
77 | "cgm",
78 | "cdr",
79 | "cmx",
80 | "dxf",
81 | "e2d",
82 | "egt",
83 | "eps",
84 | "fs",
85 | "gbr",
86 | "odg",
87 | "svg",
88 | "stl",
89 | "vrml",
90 | "x3d",
91 | "sxd",
92 | "v2d",
93 | "vnd",
94 | "wmf",
95 | "emf",
96 | "art",
97 | "xar",
98 | "png",
99 | "webp",
100 | "jxr",
101 | "hdp",
102 | "wdp",
103 | "cur",
104 | "ecw",
105 | "iff",
106 | "lbm",
107 | "liff",
108 | "nrrd",
109 | "pam",
110 | "pcx",
111 | "pgf",
112 | "sgi",
113 | "rgb",
114 | "rgba",
115 | "bw",
116 | "int",
117 | "inta",
118 | "sid",
119 | "ras",
120 | "sun",
121 | "tga"
122 | ]
123 |
--------------------------------------------------------------------------------
/src/assets/video-extensions.json:
--------------------------------------------------------------------------------
1 | [
2 | "3g2",
3 | "3gp",
4 | "aaf",
5 | "asf",
6 | "avchd",
7 | "avi",
8 | "drc",
9 | "flv",
10 | "m2v",
11 | "m4p",
12 | "m4v",
13 | "mkv",
14 | "mng",
15 | "mov",
16 | "mp2",
17 | "mp4",
18 | "mpe",
19 | "mpeg",
20 | "mpg",
21 | "mpv",
22 | "mxf",
23 | "nsv",
24 | "ogg",
25 | "ogv",
26 | "qt",
27 | "rm",
28 | "rmvb",
29 | "roq",
30 | "svi",
31 | "vob",
32 | "webm",
33 | "wmv",
34 | "yuv"
35 | ]
36 |
--------------------------------------------------------------------------------
/src/cache.native.ts:
--------------------------------------------------------------------------------
1 | import Storage from './storage';
2 | const RN = require('react-native');
3 |
4 | const storage = new Storage({ storageBackend: RN.AsyncStorage });
5 |
6 | // https://github.com/sunnylqm/react-native-storage
7 | export async function getCacheTokens(cookieName: string): Promise> {
8 | let cacheToken = false;
9 | let cacheRefreshToken = false;
10 | let cacheSession = false;
11 |
12 | try {
13 | cacheToken = await storage.load({ key: cookieName + "token" });
14 | } catch (_) {}
15 |
16 | try {
17 | cacheRefreshToken = await storage.load({ key: cookieName + "refreshToken" });
18 | } catch (_) {}
19 |
20 | try {
21 | cacheSession = await storage.load({ key: cookieName + "session" });
22 | } catch (_) {}
23 |
24 | return {
25 | cacheToken,
26 | cacheRefreshToken,
27 | cacheSession
28 | }
29 | }
30 |
31 | export async function clearCacheTokens(cookieName: string) {
32 | await storage.remove({ key: cookieName + "token" });
33 | await storage.remove({ key: cookieName + "refreshToken" });
34 | await storage.remove({ key: cookieName + "session" });
35 | }
36 |
37 | export async function setCacheTokens(g: any, cookieName: string) {
38 | await storage.save({
39 | key: cookieName + "token",
40 | data: g.token,
41 | expires: 3600 * 1000 * 24
42 | });
43 |
44 | await storage.save({
45 | key: cookieName + "refreshToken",
46 | data: g.refreshToken,
47 | expires: 3600 * 1000 * 24
48 | });
49 |
50 | await storage.save({
51 | key: cookieName + "session",
52 | data: g.session,
53 | expires: null
54 | });
55 | }
56 |
--------------------------------------------------------------------------------
/src/cache.ts:
--------------------------------------------------------------------------------
1 | import Storage from './storage';
2 |
3 | const storage = typeof window !== 'undefined' ? new Storage({ storageBackend: window.localStorage }) : undefined;
4 |
5 | // https://github.com/sunnylqm/react-native-storage
6 | export async function getCacheTokens(cookieName: string): Promise> {
7 | let cacheToken = false;
8 | let cacheRefreshToken = false;
9 | let cacheSession = false;
10 |
11 | if (storage) {
12 | try {
13 | cacheToken = await storage.load({ key: cookieName + "token" });
14 | } catch (_) { }
15 |
16 | try {
17 | cacheRefreshToken = await storage.load({ key: cookieName + "refreshToken" });
18 | } catch (_) { }
19 |
20 | try {
21 | cacheSession = await storage.load({ key: cookieName + "session" });
22 | } catch (_) { }
23 | }
24 |
25 | return {
26 | cacheToken,
27 | cacheRefreshToken,
28 | cacheSession
29 | }
30 | }
31 |
32 | export async function clearCacheTokens(cookieName: string) {
33 | if (storage) {
34 | await storage.remove({ key: cookieName + "token" });
35 | await storage.remove({ key: cookieName + "refreshToken" });
36 | await storage.remove({ key: cookieName + "session" });
37 | }
38 | }
39 |
40 | export async function setCacheTokens(g: any, cookieName: string) {
41 | if (storage) {
42 | await storage.save({
43 | key: cookieName + "token",
44 | data: g.token,
45 | expires: 3600 * 1000 * 24
46 | });
47 |
48 | await storage.save({
49 | key: cookieName + "refreshToken",
50 | data: g.refreshToken,
51 | expires: 3600 * 1000 * 24
52 | });
53 |
54 | await storage.save({
55 | key: cookieName + "session",
56 | data: g.session,
57 | expires: null
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/callFunction.tsx:
--------------------------------------------------------------------------------
1 | import { callFunction as _callFunction } from 'easybasejs';
2 |
3 | /**
4 | * @async
5 | * Call a cloud function, created in Easybase interface.
6 | * @param {string} route Route as detailed in Easybase. Found under 'Deploy'. Will be in the form of ####...####-function-name.
7 | * @param {Record} postBody Optional object to pass as the body of the POST request. This object will available in your cloud function's event.body.
8 | * @return {Promise} Response from your cloud function. Detailed with a call to 'return context.succeed("RESPONSE")'.
9 | */
10 | export const callFunction = _callFunction;
11 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | export { default as EasybaseProvider } from './EasybaseProvider';
2 | export { default as useEasybase } from './useEasybase';
3 | export { default as Auth } from './ui/Auth';
4 | // export { callFunction } from './callFunction';
5 |
--------------------------------------------------------------------------------
/src/storage/error.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Ported from https://github.com/sunnylqm/react-native-storage
3 | * Credit: Sunny Luo /sunnylqm
4 | */
5 |
6 | /**
7 | * Created by sunny on 9/1/16.
8 | */
9 |
10 | export class NotFoundError extends Error {
11 | constructor(message) {
12 | super(`Not Found! Params: ${message}`);
13 | this.name = 'NotFoundError';
14 | this.stack = new Error().stack; // Optional
15 | }
16 | }
17 | // NotFoundError.prototype = Object.create(Error.prototype);
18 |
19 | export class ExpiredError extends Error {
20 | constructor(message) {
21 | super(`Expired! Params: ${message}`);
22 | this.name = 'ExpiredError';
23 | this.stack = new Error().stack; // Optional
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/storage/index.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Ported from https://github.com/sunnylqm/react-native-storage
3 | * Credit: Sunny Luo /sunnylqm
4 | */
5 |
6 | export interface LoadParams {
7 | key: string;
8 | id?: string;
9 | autoSync?: boolean;
10 | syncInBackground?: boolean;
11 | syncParams?: any;
12 | }
13 |
14 | export class NotFoundError extends Error {
15 | name: string;
16 | }
17 |
18 | export class ExpiredError extends Error {
19 | name: string;
20 | }
21 |
22 | export default class Storage {
23 | sync: any;
24 |
25 | constructor(params?: {
26 | size?: number;
27 | storageBackend?: any;
28 | defaultExpires?: number | null;
29 | enableCache?: boolean;
30 | sync?: any;
31 | });
32 |
33 | save(params: { key: string; id?: string; data: any; expires?: number | null }): Promise;
34 |
35 | load(params: LoadParams): Promise;
36 |
37 | getIdsForKey(key: string): Promise;
38 |
39 | getAllDataForKey(key: string): Promise;
40 |
41 | getBatchData(params: LoadParams[]): Promise;
42 |
43 | getBatchDataWithIds(params: { key: string; ids: string[] }): Promise;
44 |
45 | clearMapForKey(key: string): Promise;
46 |
47 | remove(params: { key: string; id?: string }): Promise;
48 |
49 | clearMap(): Promise;
50 | }
51 |
--------------------------------------------------------------------------------
/src/types/decs.d.ts:
--------------------------------------------------------------------------------
1 | declare module "object-observer"
2 |
--------------------------------------------------------------------------------
/src/types/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/types/typings.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Default CSS definition for typescript,
3 | * will be overridden with file-specific definitions by rollup
4 | */
5 | declare module '*.css' {
6 | const content: { [className: string]: string };
7 | export default content;
8 | }
9 |
10 | interface SvgrComponent extends React.StatelessComponent> {}
11 |
12 | declare module '*.svg' {
13 | const svgUrl: string;
14 | const svgComponent: SvgrComponent;
15 | export default svgUrl;
16 | export { svgComponent as ReactComponent }
17 | }
18 |
--------------------------------------------------------------------------------
/src/ui/Auth/Auth.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, lazy, Suspense, Fragment } from 'react';
2 | import Container from './components/Container';
3 | import { ThemeProvider } from 'styled-components';
4 | import { Toaster } from 'react-hot-toast';
5 | import { mergeDeep, defaultDictionary } from '../utils';
6 | import { IStyles, IAuth } from '../uiTypes';
7 | import useEasybase from '../../useEasybase';
8 |
9 | const DefaultSignIn = lazy(() => import('./pages/SignIn'));
10 | const DefaultSignUp = lazy(() => import('./pages/SignUp'));
11 | const DefaultForgotPassword = lazy(() => import('./pages/ForgotPassword'));
12 |
13 | export default function ({ theme, customStyles, children, dictionary, signUpFields, emailTemplate }: IAuth): JSX.Element {
14 | const [themeVal, setThemeVal] = useState({});
15 |
16 | const [currentPage, setCurrentPage] = useState<"SignIn" | "SignUp" | "ForgotPassword" | "ForgotPasswordConfirm">("SignIn");
17 | const { isUserSignedIn } = useEasybase();
18 |
19 | useEffect(() => {
20 | try {
21 | document.body.style.margin = "0px";
22 | } catch (_) { }
23 | async function mounted() {
24 | // TODO: load themes from url
25 | let loadedTheme: IStyles = {};
26 | if (theme === "minimal-dark") {
27 | const _theme = (await import('../themes/minimal-dark')).default;
28 | if (_theme.init) {
29 | _theme.init()
30 | }
31 | loadedTheme = _theme;
32 | } else if (theme === "material") {
33 | const _theme = (await import('../themes/material')).default;
34 | if (_theme.init) {
35 | _theme.init()
36 | }
37 | loadedTheme = _theme;
38 | } else {
39 | // catch all
40 | const _theme = (await import('../themes/minimal')).default;
41 | if (_theme.init) {
42 | _theme.init()
43 | }
44 | loadedTheme = _theme;
45 | }
46 |
47 | if (customStyles) {
48 | loadedTheme = mergeDeep(loadedTheme, customStyles)
49 | }
50 |
51 | setThemeVal(loadedTheme)
52 | }
53 | mounted();
54 | }, [theme])
55 |
56 | const getCurrentPage = () => {
57 | switch (currentPage) {
58 | case "SignIn":
59 | return (
60 | }>
61 |
66 |
67 | )
68 | case "SignUp":
69 | return (
70 | }>
71 |
77 |
78 | )
79 | case "ForgotPassword":
80 | return (
81 | }>
82 |
87 |
88 | )
89 | default:
90 | return ;
91 | }
92 | }
93 |
94 | if (isUserSignedIn()) {
95 | return {children}
96 | } else {
97 | return (
98 |
99 |
100 |
101 | {getCurrentPage()}
102 |
103 |
104 | )
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/Container.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Container = styled.div(props => ({
5 | height: "100%",
6 | width: "100%",
7 | position: 'absolute',
8 | top: 0,
9 | left: 0,
10 | bottom: 0,
11 | right: 0,
12 | boxSizing: 'content-box',
13 | overflow: 'auto',
14 | ...(props.theme.container ? { ...props.theme.container } : {})
15 | }))
16 |
17 | export default function (props: React.HTMLAttributes) {
18 | return (
19 | {props.children}
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/EmailInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react';
2 | import { UseFormRegisterReturn } from 'react-hook-form';
3 | import Input from './internal/Input';
4 |
5 | interface ITextField extends React.InputHTMLAttributes {
6 | label?: string
7 | register(): UseFormRegisterReturn
8 | }
9 |
10 | export default function(props: ITextField) {
11 | return (
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/ErrorText.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const ErrorText = styled.p(props => ({
5 | marginTop: 5,
6 | marginBottom: -5,
7 | fontSize: 12,
8 | fontWeight: 500,
9 | color: 'red',
10 | height: 0,
11 | overflow: 'visible',
12 | ...(props.theme.errorText ? { ...props.theme.errorText } : {})
13 | }))
14 |
15 | interface IErrorText extends React.HTMLAttributes {
16 | value?: string | undefined;
17 | }
18 |
19 | export default function (props: IErrorText) {
20 | if (props.value && props.value.length) {
21 | return {props.value}
22 | }
23 | return
24 | }
25 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/ForgotPassword.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TextButton from './internal/TextButton';
3 | import styled from 'styled-components';
4 |
5 | const ForgotPassword = styled(TextButton)(props => ({
6 | marginTop: -53,
7 | marginBottom: 53,
8 | display: 'flex',
9 | ...(props.theme.forgotPassword ? { ...props.theme.forgotPassword } : {})
10 | }))
11 |
12 | export default function (props: React.ButtonHTMLAttributes) {
13 | return (
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/Form.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Form = styled.form(props => ({
5 | display: "flex",
6 | justifyContent: "center",
7 | minWidth: 300,
8 | width: 380,
9 | padding: '33px 55px',
10 | boxShadow: '0 5px 10px 0 rgb(0 0 0 / 10%)',
11 | borderRadius: 10,
12 | flexDirection: 'column',
13 | fontFamily: "inherit",
14 | margin: '6% auto 50px',
15 | '@media (max-width: 520px)': {
16 | margin: '0px !important',
17 | position: 'fixed !important',
18 | top: 0,
19 | left: 0,
20 | right: 0,
21 | bottom: 0,
22 | width: 'initial !important'
23 | },
24 | ...(props.theme.form ? { ...props.theme.form } : {})
25 | }))
26 |
27 | export default function (props: React.FormHTMLAttributes) {
28 | return (
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/GenderSelect.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { UseFormRegisterReturn } from 'react-hook-form';
3 | import styled from 'styled-components';
4 | import Label from './internal/Label';
5 | import Select from './internal/Select';
6 |
7 | const GenderSelect = styled(Select)(props => ({
8 | boxSizing: "border-box",
9 | ...(props.theme.genderSelect ? { ...props.theme.genderSelect } : {})
10 | }))
11 |
12 | const Root = styled.div({
13 | position: "relative"
14 | })
15 |
16 | interface ISelect extends React.SelectHTMLAttributes {
17 | register(): UseFormRegisterReturn;
18 | }
19 |
20 | const GenderLabel = styled(Label)(props => ({
21 | ...(props.theme.genderSelectLabel ? { ...props.theme.genderSelectLabel } : {})
22 | }))
23 |
24 | export default function (props: ISelect) {
25 | return (
26 |
27 | Gender *
28 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/HeaderText.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const HeaderText = styled.h1(props => ({
5 | fontFamily: "inherit",
6 | fontSize: 24,
7 | fontWeight: 500,
8 | letterSpacing: -.2,
9 | marginBlockStart: '0.67em',
10 | marginBlockEnd: '0.67em',
11 | marginInlineStart: 0,
12 | marginInlineEnd: 0,
13 | marginTop: '16px !important',
14 | ...(props.theme.headerText ? { ...props.theme.headerText } : {})
15 | }))
16 |
17 | export default function (props: React.HTMLAttributes) {
18 | return (
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/PasswordInput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { UseFormRegisterReturn } from 'react-hook-form';
3 | import Input from './internal/Input';
4 |
5 | interface ITextField extends React.InputHTMLAttributes {
6 | label?: string;
7 | register(): UseFormRegisterReturn;
8 | }
9 |
10 | export default function(props: ITextField) {
11 | return (
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/SecondaryButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TextButton from './internal/TextButton';
3 | import styled from 'styled-components';
4 |
5 | const SecondaryButton = styled(TextButton)(props => ({
6 | margin: '15px',
7 | ...(props.theme.secondaryButton ? { ...props.theme.secondaryButton } : {})
8 | }))
9 |
10 | export default function (props: React.ButtonHTMLAttributes) {
11 | return (
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/SecondaryText.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const SecondaryText = styled.h2(props => ({
5 | fontFamily: "inherit",
6 | fontSize: 15,
7 | fontWeight: 300,
8 | letterSpacing: -.2,
9 | lineHeight: '20px',
10 | whiteSpace: 'normal',
11 | ...(props.theme.secondaryText ? { ...props.theme.secondaryText } : {})
12 | }))
13 |
14 | export default function (props: React.HTMLAttributes) {
15 | return (
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/Spacer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | interface ISpacer {
4 | size?: "xlarge" | "large" | "medium" | "small"
5 | }
6 |
7 | export default function (props: ISpacer) {
8 | switch (props.size) {
9 | case "xlarge":
10 | return
11 | case "large":
12 | return
13 | case "small":
14 | return
15 | default:
16 | return
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/SubmitButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const SubmitButtonRoot = styled.div(props => props.theme.submitButtonRoot ? props.theme.submitButtonRoot : {});
5 |
6 | const SubmitButton = styled.button(props => ({
7 | position: 'relative',
8 | border: "none",
9 | verticalAlign: "middle",
10 | textAlign: "center",
11 | textOverflow: "ellipsis",
12 | overflow: "hidden",
13 | outline: "none",
14 | cursor: "pointer",
15 | boxSizing: 'border-box',
16 | ...(props.theme.submitButton ? { ...props.theme.submitButton } : {})
17 | }))
18 |
19 | export default function (props: React.ButtonHTMLAttributes) {
20 | return (
21 |
22 |
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/internal/Input.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { UseFormRegisterReturn } from 'react-hook-form';
3 | import styled from 'styled-components';
4 | import Label from './Label';
5 |
6 | const TextFieldRoot = styled.div(props => ({
7 | position: 'relative',
8 | width: '100%',
9 | maxWidth: '100%',
10 | padding: 0,
11 | height: 46,
12 | fontFamily: "inherit",
13 | ...(props.theme.textFieldRoot ? { ...props.theme.textFieldRoot } : {})
14 | }))
15 |
16 | const TextField = styled.input(props => ({
17 | display: "block",
18 | width: '100%',
19 | background: '0 0',
20 | border: 'none',
21 | fontFamily: "inherit",
22 | ...(props.theme.textField ? { ...props.theme.textField } : {})
23 | }))
24 |
25 | const Bar = styled.div(props => props.theme.textFieldBar ? { ...props.theme.textFieldBar } : {})
26 |
27 | interface ITextField extends React.InputHTMLAttributes {
28 | label: string
29 | register(): UseFormRegisterReturn
30 | }
31 |
32 | export default function (props: ITextField) {
33 | return (
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/internal/Label.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Label = styled.label(props => ({
5 | display: "none",
6 | fontFamily: "inherit",
7 | ...(props.theme.textFieldLabel ? { ...props.theme.textFieldLabel } : {})
8 | }))
9 |
10 | export default function (props: React.LabelHTMLAttributes) {
11 | return ()
12 | }
13 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/internal/Select.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { UseFormRegisterReturn } from 'react-hook-form';
3 | import styled from 'styled-components';
4 |
5 | const SelectContainer = styled.div({
6 | position: 'relative',
7 | display: 'inline',
8 | width: '100%',
9 | maxWidth: '100%',
10 | cursor: 'pointer',
11 | '&:after': {
12 | content: "''",
13 | width: 0,
14 | height: 0,
15 | position: 'absolute',
16 | pointerEvents: 'none',
17 | top: '.3em',
18 | right: '.75em',
19 | borderTop: '8px solid black',
20 | opacity: 0.5,
21 | borderLeft: '5px solid transparent',
22 | borderRight: '5px solid transparent'
23 | }
24 | })
25 |
26 | const Select = styled.select({
27 | WebkitAppearance: 'none',
28 | MozAppearance: 'none',
29 | appearance: 'none',
30 | padding: '1em 2em 1em 1em',
31 | border: 'none',
32 | width: '100%',
33 | fontFamily: 'inherit',
34 | fontSize: 'inherit',
35 | cursor: 'pointer',
36 | outline: 'none',
37 | '&::-ms-expand': {
38 | display: 'none'
39 | }
40 | })
41 |
42 | const SelectOption = styled.option(props => ({
43 | width: '100%',
44 | ...(props.theme.selectOption ? { ...props.theme.selectOption } : {})
45 | }))
46 |
47 | interface ISelect extends React.SelectHTMLAttributes {
48 | options: string[];
49 | id: string;
50 | register(): UseFormRegisterReturn;
51 | }
52 |
53 | export default function (props: ISelect) {
54 | return (
55 |
56 |
60 |
61 | )
62 | }
63 |
--------------------------------------------------------------------------------
/src/ui/Auth/components/internal/TextButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const TextButton = styled.button(props => ({
5 | cursor: "pointer",
6 | color: '#635bff',
7 | whiteSpace: 'nowrap',
8 | fontWeight: 500,
9 | fontSize: 14,
10 | margin: 0,
11 | background: 'none',
12 | border: 'none',
13 | ...(props.theme.textButton ? { ...props.theme.textButton } : {})
14 | }))
15 |
16 | export default function (props: React.ButtonHTMLAttributes) {
17 | return (
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/ui/Auth/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense, Fragment, lazy } from 'react';
2 | import { IAuth } from '../uiTypes';
3 |
4 | const Auth = lazy(() => import('./Auth'));
5 |
6 | export default function (props: IAuth): JSX.Element {
7 | return (
8 | }>
9 |
10 |
11 | )
12 | }
13 |
14 | /**
15 | * Note that this wrapper component exists to force code-splitting
16 | */
17 |
--------------------------------------------------------------------------------
/src/ui/Auth/pages/ForgotPassword.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Form from '../components/Form';
3 | import EmailInput from '../components/EmailInput';
4 | import HeaderText from '../components/HeaderText';
5 | import SecondaryText from '../components/SecondaryText';
6 | import SecondaryButton from '../components/SecondaryButton';
7 | import SubmitButton from '../components/SubmitButton';
8 | import Spacer from '../components/Spacer';
9 | import { useForm } from 'react-hook-form';
10 | import { IPage } from '../../uiTypes';
11 | import toast from 'react-hot-toast';
12 | import ErrorText from '../components/ErrorText';
13 | import Input from '../components/internal/Input';
14 | import PasswordInput from '../components/PasswordInput';
15 | import useEasybase from '../../../useEasybase';
16 |
17 | export default function ({ setCurrentPage, dictionary, emailTemplate }: IPage) {
18 | const [onConfirm, setOnConfirm] = useState(false);
19 | const [forgottenUsername, setForgottenUsername] = useState();
20 | const { register, handleSubmit, reset, formState: { errors, isSubmitting } } = useForm();
21 | const { forgotPassword, forgotPasswordConfirm } = useEasybase();
22 |
23 | const onSubmit = async (formData: Record) => {
24 | if (!formData.email) {
25 | return;
26 | }
27 |
28 | const forgotRes = await forgotPassword(formData.email, emailTemplate);
29 | if (forgotRes.success) {
30 | setForgottenUsername(formData.email);
31 | setOnConfirm(true);
32 | toast.success('Check your email for a verification code')
33 | } else {
34 | if (forgotRes.errorCode === "RequestLimitExceeded") {
35 | toast.error(dictionary.errorRequestLimitExceeded!);
36 | } else if (forgotRes.errorCode === "BadFormat") {
37 | reset();
38 | toast.error(dictionary.errorBadInputFormat!);
39 | } else if (forgotRes.errorCode === "NoUserExists") {
40 | reset();
41 | toast.error(dictionary.errorNoAccountFound!);
42 | } else {
43 | reset();
44 | toast.error('Bad request');
45 | }
46 | }
47 | }
48 |
49 | const onConfirmSubmit = async (formData: Record) => {
50 | if (!formData.code || !formData.newPassword || !forgottenUsername) {
51 | return;
52 | }
53 | const forgotConfirmRes = await forgotPasswordConfirm(formData.code, forgottenUsername, formData.newPassword)
54 | if (forgotConfirmRes.success) {
55 | setOnConfirm(false);
56 | setForgottenUsername("");
57 | setCurrentPage('SignIn');
58 | toast.success('Password successfully changed')
59 | } else {
60 | if (forgotConfirmRes.errorCode === "BadPasswordLength") {
61 | toast.error(dictionary.errorPasswordTooShort!);
62 | } else if (forgotConfirmRes.errorCode === "BadFormat") {
63 | reset();
64 | toast.error(dictionary.errorBadInputFormat!);
65 | } else if (forgotConfirmRes.errorCode === "NoUserExists") {
66 | reset();
67 | toast.error(dictionary.errorNoAccountFound!);
68 | } else if (forgotConfirmRes.errorCode === "WrongVerificationCode") {
69 | toast.error(dictionary.errorWrongVerificationCode!);
70 | } else {
71 | toast.error('Bad request');
72 | }
73 | }
74 | }
75 |
76 | const passwordReqs = {
77 | minLength: {
78 | value: 8,
79 | message: "Password must be at least 8 characters long"
80 | },
81 | maxLength: {
82 | value: 100,
83 | message: "Password too long"
84 | },
85 | pattern: {
86 | value: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{7,}$/gm,
87 | message: "Must contain a digit and uppercase and lowercase letters"
88 | }
89 | }
90 |
91 | const codeReqs = {
92 | minLength: {
93 | value: 8,
94 | message: "Incorrect code length"
95 | }
96 | }
97 |
98 | if (!onConfirm) {
99 | return (
100 |
113 | )
114 | } else {
115 | return (
116 |
136 | )
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/ui/Auth/pages/SignIn.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from '../components/Form';
3 | import EmailInput from '../components/EmailInput';
4 | import PasswordInput from '../components/PasswordInput';
5 | import HeaderText from '../components/HeaderText';
6 | import ForgotPassword from '../components/ForgotPassword';
7 | import SecondaryButton from '../components/SecondaryButton';
8 | import SubmitButton from '../components/SubmitButton';
9 | import Spacer from '../components/Spacer';
10 | import { useForm } from 'react-hook-form';
11 | import toast from 'react-hot-toast';
12 | import { IPage } from '../../uiTypes';
13 | import useEasybase from '../../../useEasybase';
14 |
15 | export default function ({ setCurrentPage, dictionary }: IPage) {
16 | const { register, handleSubmit, reset, formState: { isSubmitting } } = useForm();
17 | const { signIn } = useEasybase();
18 | const onSubmit = async (formData: Record) => {
19 | await new Promise(resolve => setTimeout(resolve, 1000));
20 | const signInRes = await signIn(formData.email, formData.password);
21 | if (!signInRes.success) {
22 | if (signInRes.errorCode === "NoUserExists") {
23 | toast.error(dictionary.errorUserDoesNotExist!)
24 | } else if (signInRes.errorCode === "BadFormat") {
25 | reset();
26 | toast.error(dictionary.errorBadInputFormat!)
27 | }
28 | }
29 | // Will automatically change views
30 | }
31 |
32 | return (
33 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/src/ui/Auth/pages/SignUp.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import Form from '../components/Form';
3 | import EmailInput from '../components/EmailInput';
4 | import PasswordInput from '../components/PasswordInput';
5 | import HeaderText from '../components/HeaderText';
6 | import SecondaryButton from '../components/SecondaryButton';
7 | import SubmitButton from '../components/SubmitButton';
8 | import Spacer from '../components/Spacer';
9 | import ErrorText from '../components/ErrorText';
10 | import GenderSelect from '../components/GenderSelect';
11 | import Input from '../components/internal/Input';
12 | import { useForm } from 'react-hook-form';
13 | import { IPage, ISignUpFields } from '../../uiTypes';
14 | import toast from 'react-hot-toast';
15 | import useEasybase from '../../../useEasybase';
16 |
17 | interface ISignUpPage extends IPage {
18 | signUpFields: ISignUpFields
19 | }
20 |
21 | export default function ({ setCurrentPage, dictionary, signUpFields }: ISignUpPage) {
22 | const { register, handleSubmit, formState: { errors, isSubmitting }, reset } = useForm();
23 | const { signUp, signIn } = useEasybase();
24 |
25 | const onSubmit = async (formData: Record) => {
26 | if (!formData.email || !formData.password || !formData.passwordConfirm) {
27 | return;
28 | }
29 | if (formData.password !== formData.passwordConfirm) {
30 | toast.error(dictionary.errorPasswordsDoNotMatch!);
31 | reset();
32 | return;
33 | }
34 |
35 | const signUpAttrs = { createdAt: new Date().toISOString() };
36 | for (const currField of ["firstName", "lastName", "fullName", "dateOfBirth", "gender", "phoneNumber"]) {
37 | if (signUpFields[currField]) {
38 | if (formData[currField]) {
39 | signUpAttrs[currField] = "" + formData[currField];
40 | } else {
41 | toast.error("Missing sign up field value");
42 | return;
43 | }
44 | }
45 | }
46 |
47 | const signUpRes = await signUp(formData.email, formData.password, signUpAttrs);
48 | if (signUpRes.success) {
49 | setCurrentPage("SignIn")
50 | await signIn(formData.email, formData.password)
51 | } else {
52 | if (signUpRes.errorCode === "BadFormat") {
53 | reset();
54 | toast.error(dictionary.errorBadInputFormat!);
55 | } else if (signUpRes.errorCode === "BadPasswordLength") {
56 | toast.error(dictionary.errorPasswordTooShort!);
57 | } else if (signUpRes.errorCode === "UserExists") {
58 | reset();
59 | toast.error(dictionary.errorUserAlreadyExists!);
60 | }
61 | }
62 | }
63 |
64 | const passwordReqs = {
65 | minLength: {
66 | value: 8,
67 | message: "Password must be at least 8 characters long"
68 | },
69 | maxLength: {
70 | value: 100,
71 | message: "Password too long"
72 | },
73 | pattern: {
74 | value: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{7,}$/gm,
75 | message: "Must contain a digit and uppercase and lowercase letters"
76 | }
77 | }
78 |
79 | return (
80 |
185 | )
186 | }
187 |
--------------------------------------------------------------------------------
/src/ui/NativeAuth/NativeAuth.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, lazy, Suspense, Fragment, useEffect } from 'react';
2 | import { ThemeProvider } from 'styled-components/native';
3 | import { defaultDictionary } from '../utils';
4 | import { INativeAuth } from '../uiTypes';
5 | import { Toast, Container, StatusBar } from './components';
6 |
7 | const { useEasybase } = require('easybase-react');
8 |
9 | const DefaultSignIn = lazy(() => import('./pages/SignIn'));
10 | const DefaultSignUp = lazy(() => import('./pages/SignUp'));
11 | const DefaultForgotPassword = lazy(() => import('./pages/ForgotPassword'));
12 |
13 | export default function ({ customStyles, children, dictionary, signUpFields, emailTemplate }: INativeAuth): JSX.Element {
14 | const [currentPage, setCurrentPage] = useState<"SignIn" | "SignUp" | "ForgotPassword" | "ForgotPasswordConfirm">("SignIn");
15 | const [toastOpen, setToastOpen] = useState(false);
16 | const [toastMessage, setToastMessage] = useState("");
17 |
18 | const { isUserSignedIn } = useEasybase();
19 |
20 | const toast = (message: string) => {
21 | setToastMessage(message);
22 | setToastOpen(true);
23 | }
24 |
25 | useEffect(() => {
26 | if (toastOpen) {
27 | setToastOpen(false)
28 | }
29 | }, [currentPage])
30 |
31 | const getCurrentPage = () => {
32 | switch (currentPage) {
33 | case "SignIn":
34 | return (
35 | }>
36 |
42 |
43 | )
44 | case "SignUp":
45 | return (
46 | }>
47 |
54 |
55 | )
56 | case "ForgotPassword":
57 | return (
58 | }>
59 |
65 |
66 | )
67 | default:
68 | return ;
69 | }
70 | }
71 |
72 | if (isUserSignedIn()) {
73 | return {children}
74 | } else {
75 | return (
76 |
77 |
78 |
79 |
80 | {getCurrentPage()}
81 |
82 |
83 | )
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/ui/NativeAuth/components.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import styled from 'styled-components/native';
3 |
4 | export const StatusBar = styled.StatusBar((props: any) => ({
5 | backgroundColor: '#fff',
6 | ...(props.theme.statusBar ? { ...props.theme.statusBar } : {})
7 | }))
8 |
9 | export const View = styled.View({
10 | width: "100%"
11 | });
12 |
13 | export const MainView = styled(View)({
14 | width: "100%",
15 | marginTop: 60,
16 | marginBottom: 60
17 | });
18 |
19 | export const Container = styled.KeyboardAvoidingView((props: any) => ({
20 | flex: 1,
21 | backgroundColor: '#fff',
22 | ...(props.theme.container ? { ...props.theme.container } : {})
23 | }))
24 |
25 | export const ScrollRoot = styled.ScrollView((props: any) => ({
26 | flex: 1,
27 | ...(props.theme.form ? { ...props.theme.form } : {})
28 | }))
29 |
30 | export const Form = (props: any) =>
42 |
43 | {props.children}
44 |
45 |
46 |
47 | export const HeaderText = styled.Text((props: any) => ({
48 | fontSize: 37,
49 | fontWeight: "bold",
50 | marginTop: 25,
51 | color: '#13151b',
52 | width: '100%',
53 | textAlign: "left",
54 | ...(props.theme.headerText ? { ...props.theme.headerText } : {})
55 | }))
56 |
57 | export const SecondaryText = styled.Text((props: any) => ({
58 | fontSize: 18,
59 | fontWeight: '300',
60 | textAlign: "left",
61 | color: '#3c4257',
62 | width: '100%',
63 | ...(props.theme.secondaryText ? { ...props.theme.secondaryText } : {})
64 | }))
65 |
66 | export const SpacerXL = styled.View((props: any) => ({
67 | height: 64
68 | }))
69 |
70 | export const SpacerL = styled.View((props: any) => ({
71 | height: 58
72 | }))
73 |
74 | export const SpacerS = styled.View((props: any) => ({
75 | height: 24
76 | }))
77 |
78 | export const Spacer = styled.View((props: any) => ({
79 | height: 37
80 | }))
81 |
82 | export const Input = styled.TextInput((props: any) => ({
83 | width: '100%',
84 | maxWidth: '100%',
85 | height: 60,
86 | borderColor: '#dbdbdb',
87 | borderStyle: 'solid',
88 | borderWidth: 1,
89 | fontWeight: 400,
90 | fontSize: 20,
91 | color: '#3c4257',
92 | background: 'transparent',
93 | borderRadius: 4,
94 | padding: 12,
95 | ...(props.theme.textField ? { ...props.theme.textField } : {})
96 | }))
97 |
98 | export const NoSecondaryButton = styled.View(() => ({
99 | backgroundColor: 'transparent',
100 | height: 35,
101 | overflow: "visible",
102 | marginBottom: '10%'
103 | }))
104 |
105 | const SecondaryButtonRoot = styled.TouchableOpacity((props: any) => ({
106 | backgroundColor: 'transparent',
107 | height: 35,
108 | overflow: "visible",
109 | marginBottom: '10%',
110 | ...(props.theme.secondaryButtonRoot ? { ...props.theme.secondaryButtonRoot } : {})
111 | }))
112 |
113 | const SecondaryButtonText = styled.Text((props: any) => ({
114 | fontWeight: 'bold',
115 | fontSize: 20,
116 | color: '#534eff',
117 | textAlign: "center",
118 | ...(props.theme.secondaryButton ? { ...props.theme.secondaryButton } : {})
119 | }))
120 |
121 | export const SecondaryButton = (props: any) => {props.title}
122 |
123 | const ForgotButtonRoot = styled(SecondaryButtonRoot)((props: any) => ({
124 | width: "100%",
125 | marginBottom: -35,
126 | ...(props.theme.forgotPasswordRoot ? { ...props.theme.forgotPasswordRoot } : {})
127 | }))
128 |
129 | const ForgotButtonText = styled(SecondaryButtonText)((props: any) => ({
130 | textAlign: "left",
131 | marginTop: 15,
132 | marginBottom: -15,
133 | fontSize: 16,
134 | ...(props.theme.forgotPassword ? { ...props.theme.forgotPassword } : {})
135 | }))
136 |
137 | export const ForgotPassword = (props: any) => {props.title}
138 |
139 | const SubmitButtonRoot = styled.TouchableOpacity((props: any) => ({
140 | width: '100%',
141 | backgroundColor: 'rgb(99, 91, 255)',
142 | height: 60,
143 | borderRadius: 4,
144 | borderColor: '#534eff',
145 | borderStyle: 'solid',
146 | borderWidth: 1,
147 | alignItems: 'center',
148 | justifyContent: 'center',
149 | ...(props.theme.submitButtonRoot ? { ...props.theme.submitButtonRoot } : {})
150 | }))
151 |
152 | const SubmitButtonText = styled.Text((props: any) => ({
153 | fontWeight: 'bold',
154 | fontSize: 20,
155 | color: "#FFFFFF",
156 | ...(props.theme.submitButton ? { ...props.theme.submitButton } : {})
157 | }))
158 |
159 | export const SubmitButton = (props: any) => {props.title}
160 |
161 | const StyledErrorText = styled.Text((props: any) => ({
162 | height: 50,
163 | marginBottom: -55,
164 | overflow: "visible",
165 | marginTop: 5,
166 | fontSize: 14,
167 | fontWeight: '500',
168 | color: '#FF0000',
169 | ...(props.theme.errorText ? { ...props.theme.errorText } : {})
170 | }))
171 |
172 | export const ErrorText = (props: any) => props.value ? {props.value} :
173 |
174 | export const Picker = styled.Picker((props: any) => ({
175 | width: '100%',
176 | maxWidth: '100%',
177 | height: 46,
178 | borderColor: '#dbdbdb',
179 | borderStyle: 'solid',
180 | borderWidth: 1,
181 | fontWeight: 400,
182 | fontSize: 16,
183 | background: 'transparent',
184 | borderRadius: 4,
185 | padding: 12,
186 | ...(props.theme.picker ? { ...props.theme.picker } : {})
187 | }))
188 |
189 | const ToastRoot = styled.View((props: any) => ({
190 | position: "absolute",
191 | top: 47,
192 | left: 0,
193 | right: 0,
194 | flex: 1,
195 | justifyContent: "center",
196 | alignItems: "center",
197 | zIndex: 1000,
198 | ...(props.theme.toast ? { ...props.theme.toast } : {})
199 | }))
200 |
201 | const ToastContainer = styled.TouchableOpacity((props: any) => ({
202 | backgroundColor: '#333',
203 | elevation: '6',
204 | justifyContent: 'center',
205 | alignItems: 'center',
206 | flexDirection: 'row',
207 | borderRadius: 26,
208 | maxWidth: "94%",
209 | ...(props.theme.toast ? { ...props.theme.toast } : {})
210 | }))
211 |
212 | const ToastText = styled.Text((props: any) => ({
213 | marginLeft: 25,
214 | marginRight: 25,
215 | marginTop: 10,
216 | marginBottom: 10,
217 | fontSize: 17,
218 | color: "#fff",
219 | textAlign: 'center',
220 | ...(props.theme.toastText ? { ...props.theme.toastText } : {})
221 | }))
222 |
223 | const CloseToastText = styled.Text((props: any) => ({
224 | color: "#AAA",
225 | fontWeight: '600',
226 | fontSize: 15,
227 | marginRight: 25,
228 | ...(props.theme.closeToastText ? { ...props.theme.closeToastText } : {})
229 | }))
230 |
231 | export const Toast = ({ toastMessage, toastOpen, setToastOpen }: { toastMessage: string, toastOpen: boolean, setToastOpen: React.Dispatch> }) => {
232 | if (!toastOpen) return
233 |
234 | return (
235 |
236 | setToastOpen(false)} style={{ shadowColor: "#000", shadowOffset: { width: 0, height: 3 }, shadowOpacity: 0.27, shadowRadius: 4.65 }}>
237 | {toastMessage}
238 | ✕
239 |
240 |
241 | )
242 | }
243 |
--------------------------------------------------------------------------------
/src/ui/NativeAuth/pages/ForgotPassword.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { useForm, Controller } from 'react-hook-form';
3 | import { INativePage } from '../../uiTypes';
4 | import { Form, HeaderText, View, Input, SpacerXL, SubmitButton, SpacerS, SecondaryButton, ErrorText, SecondaryText, MainView, NoSecondaryButton } from '../components';
5 |
6 | const { useEasybase } = require('easybase-react');
7 |
8 | export default function ({ setCurrentPage, dictionary, toast, emailTemplate }: INativePage) {
9 | const [onConfirm, setOnConfirm] = useState(false);
10 | const [forgottenUsername, setForgottenUsername] = useState();
11 | const { control, handleSubmit, reset, formState: { isSubmitting, errors } } = useForm();
12 | const { forgotPassword, forgotPasswordConfirm } = useEasybase();
13 |
14 | const onSubmit = async (formData: Record) => {
15 | if (!formData.email) {
16 | return;
17 | }
18 |
19 | const forgotRes = await forgotPassword(formData.email, emailTemplate);
20 | if (forgotRes.success) {
21 | setForgottenUsername(formData.email);
22 | setOnConfirm(true);
23 | toast('Check your email for a verification code')
24 | } else {
25 | if (forgotRes.errorCode === "RequestLimitExceeded") {
26 | toast(dictionary.errorRequestLimitExceeded!);
27 | } else if (forgotRes.errorCode === "BadFormat") {
28 | reset();
29 | toast(dictionary.errorBadInputFormat!);
30 | } else if (forgotRes.errorCode === "NoUserExists") {
31 | reset();
32 | toast(dictionary.errorNoAccountFound!);
33 | } else {
34 | reset();
35 | toast('Bad request');
36 | }
37 | }
38 | }
39 |
40 | const onConfirmSubmit = async (formData: Record) => {
41 | if (!formData.code || !formData.newPassword || !forgottenUsername) {
42 | return;
43 | }
44 | const forgotConfirmRes = await forgotPasswordConfirm(formData.code, forgottenUsername, formData.newPassword)
45 | if (forgotConfirmRes.success) {
46 | setOnConfirm(false);
47 | setForgottenUsername("");
48 | setCurrentPage('SignIn');
49 | toast('Password successfully changed')
50 | } else {
51 | if (forgotConfirmRes.errorCode === "BadPasswordLength") {
52 | toast(dictionary.errorPasswordTooShort!);
53 | } else if (forgotConfirmRes.errorCode === "BadFormat") {
54 | reset();
55 | toast(dictionary.errorBadInputFormat!);
56 | } else if (forgotConfirmRes.errorCode === "NoUserExists") {
57 | reset();
58 | toast(dictionary.errorNoAccountFound!);
59 | } else if (forgotConfirmRes.errorCode === "WrongVerificationCode") {
60 | toast(dictionary.errorWrongVerificationCode!);
61 | } else {
62 | toast('Bad request');
63 | }
64 | }
65 | }
66 |
67 | const passwordReqs = {
68 | minLength: {
69 | value: 8,
70 | message: "Password must be at least 8 characters long"
71 | },
72 | maxLength: {
73 | value: 100,
74 | message: "Password too long"
75 | },
76 | pattern: {
77 | value: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{7,}$/gm,
78 | message: "Must contain a digit and uppercase and lowercase letters"
79 | }
80 | }
81 |
82 | const codeReqs = {
83 | minLength: {
84 | value: 8,
85 | message: "Incorrect code length"
86 | }
87 | }
88 |
89 | if (!onConfirm) {
90 | return (
91 |
123 | )
124 | } else {
125 | return (
126 |
177 | )
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/ui/NativeAuth/pages/SignIn.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useForm, Controller } from 'react-hook-form';
3 | import { INativePage } from '../../uiTypes';
4 | import { Form, HeaderText, View, Input, SubmitButton, SecondaryButton, ForgotPassword, SpacerS, SpacerXL, MainView } from '../components';
5 |
6 | const { useEasybase } = require('easybase-react');
7 |
8 | export default function ({ setCurrentPage, dictionary, toast }: INativePage) {
9 | const { control, handleSubmit, reset, formState: { isSubmitting } } = useForm();
10 | const { signIn } = useEasybase();
11 |
12 | const onSubmit = async (formData: Record) => {
13 | if (!formData.email || !formData.password) {
14 | return;
15 | }
16 |
17 | const signInRes = await signIn(formData.email, formData.password);
18 | if (!signInRes.success) {
19 | if (signInRes.errorCode === "NoUserExists") {
20 | toast(dictionary.errorUserDoesNotExist!)
21 | } else if (signInRes.errorCode === "BadFormat") {
22 | reset();
23 | toast(dictionary.errorBadInputFormat!)
24 | }
25 | }
26 | // Will automatically change views
27 | }
28 |
29 | return (
30 |
80 | )
81 | }
82 |
--------------------------------------------------------------------------------
/src/ui/ReactNative.tsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense, Fragment, lazy } from 'react';
2 | import { INativeAuth } from './uiTypes';
3 |
4 | const NativeAuthComp = lazy(() => import('./NativeAuth/NativeAuth'));
5 |
6 | export function NativeAuth(props: INativeAuth): JSX.Element {
7 | return (
8 | }>
9 |
10 |
11 | )
12 | }
13 |
14 | /**
15 | * Note that this wrapper component exists to force code-splitting
16 | */
17 |
--------------------------------------------------------------------------------
/src/ui/themes/material.ts:
--------------------------------------------------------------------------------
1 | import { IStyles } from "../uiTypes";
2 |
3 | export default {
4 | init: () => {
5 | if (!document.getElementById('easybase-material-roboto')) {
6 | const roboto = document.createElement('link');
7 | roboto.setAttribute('href', 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
8 | roboto.setAttribute('rel', 'stylesheet');
9 | roboto.setAttribute('id', 'easybase-material-roboto')
10 | document.head.appendChild(roboto);
11 | }
12 | },
13 | container: {
14 | fontFamily: '"Roboto","Segoe UI",BlinkMacSystemFont,system-ui,-apple-system,sans-serif',
15 | backgroundColor: '#fbfbfb'
16 | },
17 | form: {
18 | backgroundColor: "#FFF"
19 | },
20 | submitButton: {
21 | // https://codepen.io/finnhvman/pen/MQyJxV
22 | boxShadow: '0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12)',
23 | color: "rgb(255, 255, 255)",
24 | backgroundColor: "rgb(33, 150, 243)",
25 | transition: 'box-shadow 0.2s',
26 | borderRadius: 4,
27 | padding: "0 16px",
28 | minWidth: 64,
29 | height: 40,
30 | fontSize: 17,
31 | fontWeight: 500,
32 | width: '100%',
33 | '&:hover': {
34 | boxShadow: '0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12)',
35 | '&::before': {
36 | opacity: 0.08
37 | },
38 | '&:focus::before': {
39 | opacity: 0.3
40 | }
41 | },
42 | '&:focus': {
43 | boxShadow: '0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12)',
44 | '&::before': {
45 | opacity: 0.24
46 | }
47 | },
48 | '&::before': {
49 | content: "''", // Note two outer string
50 | position: 'absolute',
51 | top: 0,
52 | bottom: 0,
53 | left: 0,
54 | right: 0,
55 | backgroundColor: 'rgb(, 255, 255, 255)',
56 | opacity: 0,
57 | transition: 'opacity 0.2s'
58 | },
59 | '&::after': {
60 | content: "''", // Note two outer string
61 | position: 'absolute',
62 | left: '50%',
63 | top: '50%',
64 | borderRadius: '50%',
65 | padding: '50%',
66 | width: 32,
67 | height: 32,
68 | backgroundColor: 'rgb(255, 255, 255)',
69 | opacity: 0,
70 | transform: 'translate(-50%, -50%) scale(1)',
71 | transition: 'opacity 1s, transform 0.5s'
72 | },
73 | '&:active': {
74 | boxShadow: '0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12)',
75 | '&::after': {
76 | opacity: 0.32,
77 | transform: 'translate(-50%, -50%) scale(0)',
78 | transition: 'transform 0s'
79 | }
80 | },
81 | '&:disabled': {
82 | color: 'rgba(0, 0, 0, 0.38)',
83 | backgroundColor: 'rgba(0, 0, 0, 0.12)',
84 | boxShadow: 'none',
85 | cursor: 'initial',
86 | '&::before': {
87 | opacity: 0
88 | },
89 | '&::after': {
90 | opacity: 0
91 | }
92 | }
93 | },
94 | textField: {
95 | fontSize: 16,
96 | color: '#555',
97 | lineHeight: 1.2,
98 | height: 45,
99 | padding: '0 5px',
100 | borderRadius: 0,
101 | borderBottom: '1px solid rgba(255, 255, 255, 0.7)',
102 | '&:focus': {
103 | outline: "none"
104 | },
105 | '&:focus ~ label, &:valid ~ label, &:not(:placeholder-shown) ~ label': {
106 | top: -14,
107 | fontSize: 12,
108 | color: '#2196F3'
109 | },
110 | '&:focus ~ div:before': {
111 | width: '100%'
112 | }
113 | },
114 | textFieldBar: {
115 | position: 'relative',
116 | display: 'block',
117 | width: '100%',
118 | '&:before': {
119 | content: "''",
120 | height: 2,
121 | width: 0,
122 | bottom: -2,
123 | position: 'absolute',
124 | background: '#2196F3',
125 | transition: '300ms ease all',
126 | left: '0%'
127 | }
128 | },
129 | textFieldLabel: {
130 | display: 'block',
131 | color: '#878787',
132 | fontSize: 16,
133 | position: 'absolute',
134 | pointerEvents: 'none',
135 | left: 5,
136 | top: 10,
137 | transition: '300ms ease all'
138 | },
139 | textFieldRoot: {
140 | borderBottom: '2px solid #adadad',
141 | marginTop: -10
142 | },
143 | secondaryButton: {
144 | color: '#2196F3'
145 | },
146 | forgotPassword: {
147 | color: '#2196F3'
148 | },
149 | errorText: {
150 | color: '#ff0000'
151 | },
152 | genderSelect: {
153 | borderBottom: '2px solid #adadad'
154 | },
155 | genderSelectLabel: {
156 | color: '#2196F3',
157 | display: 'block',
158 | top: -14,
159 | fontSize: 12
160 | }
161 | } as IStyles
162 |
--------------------------------------------------------------------------------
/src/ui/themes/minimal-dark.ts:
--------------------------------------------------------------------------------
1 | import { IStyles } from "../uiTypes";
2 |
3 | export default {
4 | init: () => {},
5 | container: {
6 | fontFamily: '-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Ubuntu,sans-serif',
7 | backgroundColor: '#101010'
8 | },
9 | textFieldRoot: {
10 | backgroundColor: "#212121",
11 | height: 46,
12 | borderRadius: 4,
13 | cursor: "text",
14 | display: 'inline-flex',
15 | outline: 0,
16 | position: 'relative',
17 | color: '#FFF'
18 | },
19 | textField: {
20 | whiteSpace: 'nowrap',
21 | fontWeight: 400,
22 | fontSize: 16,
23 | height: 34,
24 | display: 'inline-flex',
25 | backgroundColor: 'rgba(255, 255, 255, 0.09)',
26 | transition: 'color .24s,background-color .24s,box-shadow .24s',
27 | boxShadow: 'rgb(60 66 87 / 16%) 0px 0px 0px 1px',
28 | outline: 'none',
29 | borderRadius: 4,
30 | padding: '6px 12px',
31 | color: '#FFF',
32 | '&:active, &:focus': {
33 | boxShadow: 'rgb(58 151 212 / 36%) 0px 0px 0px 4px, rgb(60 66 87 / 16%) 0px 0px 0px 1px'
34 | }
35 | },
36 | textFieldLabel: {
37 | display: "block",
38 | position: "absolute",
39 | top: -27,
40 | left: 0,
41 | fontSize: 14,
42 | color: "rgba(255, 255, 255, 0.7)"
43 | },
44 | submitButton: {
45 | width: '100%',
46 | backgroundColor: 'rgb(99, 91, 255)',
47 | boxShadow: 'rgb(0 0 0 / 12%) 0px 1px 1px 0px, rgb(69 56 255 / 80%) 0px 0px 0px 1px, rgb(60 66 87 / 8%) 0px 2px 5px 0px',
48 | height: 44,
49 | transition: 'background-color .24s,box-shadow .24s',
50 | fontWeight: 500,
51 | fontSize: 16,
52 | borderRadius: 4,
53 | color: 'rgba(0, 0, 0, 0.87)',
54 | '&:focus, &:active': {
55 | boxShadow: 'rgb(58 151 212 / 36%) 0px 0px 0px 4px, rgb(0 0 0 / 12%) 0px 1px 1px 0px, rgb(69 56 255 / 80%) 0px 0px 0px 1px, rgb(60 66 87 / 8%) 0px 2px 5px 0px'
56 | }
57 | },
58 | genderSelect: {
59 | fontWeight: 400,
60 | fontSize: 16,
61 | height: 46,
62 | transition: 'color .24s,background-color .24s,box-shadow .24s',
63 | boxShadow: 'rgb(60 66 87 / 16%) 0px 0px 0px 1px',
64 | padding: '6px 12px',
65 | borderRadius: 4,
66 | backgroundColor: 'rgba(255, 255, 255, 0.09)',
67 | color: "#fff",
68 | '&:active, &:focus': {
69 | boxShadow: 'rgb(58 151 212 / 36%) 0px 0px 0px 4px, rgb(60 66 87 / 16%) 0px 0px 0px 1px'
70 | }
71 | },
72 | selectOption: {
73 | background: '#000000'
74 | },
75 | toast: {
76 | borderRadius: '10px',
77 | color: '#FFF',
78 | backgroundColor: '#242424'
79 | },
80 | form: {
81 | backgroundColor: '#242424'
82 | },
83 | headerText: {
84 | color: '#FFF'
85 | },
86 | secondaryText: {
87 | color: '#FFF'
88 | }
89 | } as IStyles
90 |
--------------------------------------------------------------------------------
/src/ui/themes/minimal.ts:
--------------------------------------------------------------------------------
1 | import { IStyles } from "../uiTypes";
2 |
3 | export default {
4 | init: () => {},
5 | container: {
6 | fontFamily: '-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Ubuntu,sans-serif',
7 | backgroundColor: '#fbfbfb'
8 | },
9 | textFieldRoot: {
10 | backgroundColor: "#FFF",
11 | height: 46,
12 | borderRadius: 4,
13 | cursor: "text",
14 | display: 'inline-flex',
15 | outline: 0,
16 | position: 'relative'
17 | },
18 | textField: {
19 | whiteSpace: 'nowrap',
20 | fontWeight: 400,
21 | fontSize: 16,
22 | height: 34,
23 | display: 'inline-flex',
24 | background: 'transparent',
25 | transition: 'color .24s,background-color .24s,box-shadow .24s',
26 | boxShadow: 'rgb(60 66 87 / 16%) 0px 0px 0px 1px',
27 | outline: 'none',
28 | borderRadius: 4,
29 | padding: '6px 12px',
30 | '&:active, &:focus': {
31 | boxShadow: 'rgb(58 151 212 / 36%) 0px 0px 0px 4px, rgb(60 66 87 / 16%) 0px 0px 0px 1px'
32 | }
33 | },
34 | textFieldLabel: {
35 | display: "block",
36 | position: "absolute",
37 | top: -27,
38 | left: 0,
39 | fontSize: 14,
40 | color: "#3c4257"
41 | },
42 | submitButton: {
43 | width: '100%',
44 | backgroundColor: 'rgb(99, 91, 255)',
45 | boxShadow: 'rgb(0 0 0 / 12%) 0px 1px 1px 0px, rgb(69 56 255 / 80%) 0px 0px 0px 1px, rgb(60 66 87 / 8%) 0px 2px 5px 0px',
46 | height: 44,
47 | transition: 'background-color .24s,box-shadow .24s',
48 | fontWeight: 500,
49 | fontSize: 16,
50 | borderRadius: 4,
51 | color: '#fff',
52 | '&:focus, &:active': {
53 | boxShadow: 'rgb(58 151 212 / 36%) 0px 0px 0px 4px, rgb(0 0 0 / 12%) 0px 1px 1px 0px, rgb(69 56 255 / 80%) 0px 0px 0px 1px, rgb(60 66 87 / 8%) 0px 2px 5px 0px'
54 | }
55 | },
56 | genderSelect: {
57 | fontWeight: 400,
58 | fontSize: 16,
59 | height: 46,
60 | transition: 'color .24s,background-color .24s,box-shadow .24s',
61 | boxShadow: 'rgb(60 66 87 / 16%) 0px 0px 0px 1px',
62 | padding: '6px 12px',
63 | borderRadius: 4,
64 | '&:active, &:focus': {
65 | boxShadow: 'rgb(58 151 212 / 36%) 0px 0px 0px 4px, rgb(60 66 87 / 16%) 0px 0px 0px 1px'
66 | }
67 | },
68 | toast: {
69 | borderRadius: '10px'
70 | },
71 | form: {
72 | backgroundColor: "#FFF"
73 | },
74 | headerText: {
75 | color: '#3c4257'
76 | },
77 | secondaryText: {
78 | color: '#3c4257'
79 | }
80 | } as IStyles
81 |
--------------------------------------------------------------------------------
/src/ui/uiTypes.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { EmailTemplate } from "../types/types";
3 |
4 | export interface IDictionary {
5 | /**
6 | * SignUp
7 | */
8 | newPasswordLabel?: string;
9 | confirmNewPasswordLabel?: string;
10 | newEmailLabel?: string;
11 | signUpSubmitButton?: string;
12 | backToSignIn?: string;
13 | signUpHeader?: string;
14 | newFirstNameLabel?: string;
15 | newLastNameLabel?: string;
16 | newFullNameLabel?: string;
17 | newDateOfBirthLabel?: string;
18 | newPhoneNumberLabel?: string;
19 |
20 | /**
21 | * SignIn
22 | */
23 | signInHeader?: string;
24 | emailLabel?: string;
25 | passwordLabel?: string;
26 | forgotPasswordButton?: string;
27 | signInSubmitButton?: string;
28 | noAccountButton?: string;
29 |
30 | /**
31 | * ForgotPassword
32 | */
33 | forgotPasswordHeader?: string;
34 | forgotPasswordConfirmHeader?: string;
35 | forgotPasswordSecondaryHeader?: string;
36 | forgotPasswordConfirmSubmitButton?: string;
37 | forgotPasswordSubmitButton?: string;
38 | codeLabel?: string;
39 | forgotPasswordConfirmLabel?: string;
40 |
41 | /**
42 | * Errors
43 | */
44 | errorPasswordsDoNotMatch?: string;
45 | errorBadInputFormat?: string;
46 | errorPasswordTooShort?: string;
47 | errorUserAlreadyExists?: string;
48 | errorUserDoesNotExist?: string;
49 | errorRequestLimitExceeded?: string;
50 | errorNoAccountFound?: string;
51 | errorWrongVerificationCode?: string;
52 |
53 | }
54 |
55 | export interface IPage {
56 | setCurrentPage: React.Dispatch>;
57 | dictionary: IDictionary;
58 | emailTemplate?: EmailTemplate;
59 | }
60 |
61 | export interface INativePage {
62 | setCurrentPage: React.Dispatch>;
63 | dictionary: IDictionary;
64 | toast: (message: string) => void;
65 | emailTemplate?: EmailTemplate;
66 | }
67 |
68 | export interface IStyles {
69 | init?: () => any;
70 | container?: Record;
71 | textFieldRoot?: Record;
72 | textField?: Record;
73 | textFieldLabel?: Record;
74 | submitButton?: Record;
75 | genderSelect?: Record;
76 | genderSelectLabel?: Record;
77 | toast?: Record;
78 | form?: Record;
79 | headerText?: Record;
80 | secondaryText?: Record;
81 | secondaryButton?: Record;
82 | errorText?: Record;
83 | textButton?: Record;
84 | selectOption?: Record;
85 | textFieldBar?: Record;
86 | forgotPassword?: Record;
87 | }
88 |
89 | export interface INativeStyles {
90 | container?: Record;
91 | form?: Record;
92 | headerText?: Record;
93 | secondaryButton?: Record;
94 | secondaryButtonRoot?: Record;
95 | secondaryText?: Record;
96 | submitButton?: Record;
97 | submitButtonRoot?: Record;
98 | textField?: Record;
99 | errorText?: Record;
100 | forgotPassword?: Record;
101 | forgotPasswordRoot?: Record;
102 | picker?: Record;
103 | toast?: Record;
104 | toastText?: Record;
105 | closeToastText?: Record;
106 | }
107 |
108 | export interface IAuth {
109 | children: React.ReactNode;
110 | /** Theme for consistent styling, defaults to 'minimal' */
111 | theme?: "minimal" | "minimal-dark" | "material";
112 | /** Override specific styles for components of an Auth instance */
113 | customStyles?: IStyles;
114 | /** Edit specific language across the Auth instance components */
115 | dictionary?: IDictionary;
116 | /** Extra fields that will be shown in the SignUp view of . These fields are shown in addition Email, Password, and Confirm Password. */
117 | signUpFields?: ISignUpFields;
118 | /** Optional settings for the formatting & content of the verification emails */
119 | emailTemplate?: EmailTemplate;
120 | }
121 |
122 | export interface INativeAuth {
123 | children: React.ReactNode;
124 | /** Override specific styles for components of an Auth instance */
125 | customStyles?: INativeStyles;
126 | /** Edit specific language across the Auth instance components */
127 | dictionary?: IDictionary;
128 | /** Extra fields that will be shown in the SignUp view of . These fields are shown in addition Email, Password, and Confirm Password. */
129 | signUpFields?: ISignUpFields;
130 | /** Optional settings for the formatting & content of the verification emails */
131 | emailTemplate?: EmailTemplate;
132 | }
133 |
134 | type ValidationValueMessage<
135 | TValidationValue extends ValidationValue = ValidationValue,
136 | > = {
137 | value: TValidationValue;
138 | message: string;
139 | };
140 |
141 | type ValidationValue = boolean | number | string | RegExp;
142 |
143 | type ValidationRule<
144 | TValidationValue extends ValidationValue = ValidationValue,
145 | > = TValidationValue | ValidationValueMessage;
146 |
147 | export interface ISignUpFieldValidators {
148 | required?: string | ValidationRule;
149 | min?: ValidationRule;
150 | max?: ValidationRule;
151 | maxLength?: ValidationRule;
152 | minLength?: ValidationRule;
153 | pattern?: ValidationRule;
154 | }
155 |
156 | export interface ISignUpFields {
157 | firstName?: boolean | ISignUpFieldValidators;
158 | lastName?: boolean | ISignUpFieldValidators;
159 | fullName?: boolean | ISignUpFieldValidators;
160 | dateOfBirth?: boolean | ISignUpFieldValidators;
161 | gender?: boolean | ISignUpFieldValidators;
162 | phoneNumber?: boolean | ISignUpFieldValidators;
163 | }
164 |
165 | // TODO: allow custom components for individual Auth views.
166 | // export interface ICustomViews {
167 | // SignIn?: JSX.Element;
168 | // SignUp?: JSX.Element;
169 | // ForgotPassword?: JSX.Element;
170 | // ForgotPasswordConfirm?: JSX.Element;
171 | // Fixed?: JSX.Element;
172 | // }
173 |
--------------------------------------------------------------------------------
/src/ui/utils.tsx:
--------------------------------------------------------------------------------
1 | import { IDictionary } from "./uiTypes";
2 |
3 | /**
4 | * https://stackoverflow.com/a/48218209
5 | *
6 | * Performs a deep merge of objects and returns new object. Does not modify
7 | * objects (immutable) and merges arrays via concatenation.
8 | *
9 | * @param {...object} objects - Objects to merge
10 | * @returns {object} New object with merged key/values
11 | */
12 | export function mergeDeep(...objects: any[]) {
13 | const isObject = (obj: any) => obj && typeof obj === 'object';
14 |
15 | return objects.reduce((prev, obj) => {
16 | Object.keys(obj).forEach(key => {
17 | const pVal = prev[key];
18 | const oVal = obj[key];
19 |
20 | if (Array.isArray(pVal) && Array.isArray(oVal)) {
21 | prev[key] = pVal.concat(...oVal);
22 | } else if (isObject(pVal) && isObject(oVal)) {
23 | prev[key] = mergeDeep(pVal, oVal);
24 | } else {
25 | prev[key] = oVal;
26 | }
27 | });
28 |
29 | return prev;
30 | }, {});
31 | }
32 |
33 | export const defaultDictionary: IDictionary = {
34 | newPasswordLabel: "Password *",
35 | confirmNewPasswordLabel: "Confirm Password *",
36 | newEmailLabel: "Email *",
37 | signUpSubmitButton: "Continue",
38 | backToSignIn: "Back to Sign In",
39 | signUpHeader: "Create your account",
40 | newFirstNameLabel: "First Name *",
41 | newLastNameLabel: "Last Name *",
42 | newFullNameLabel: "Full Name *",
43 | newDateOfBirthLabel: "Date of Birth *",
44 | newPhoneNumberLabel: "Phone Number *",
45 |
46 | /**
47 | * SignIn
48 | */
49 | signInHeader: "Sign in to your account",
50 | emailLabel: "Email",
51 | passwordLabel: "Password",
52 | forgotPasswordButton: "Forgot Your Password?",
53 | signInSubmitButton: "Continue",
54 | noAccountButton: "No Account? Sign Up",
55 |
56 | /**
57 | * ForgotPassword
58 | */
59 | forgotPasswordHeader: "Reset your password",
60 | forgotPasswordConfirmHeader: "Reset your password",
61 | forgotPasswordSecondaryHeader: "Enter your email address and we will send you a verification code.",
62 | forgotPasswordConfirmSubmitButton: "Continue",
63 | forgotPasswordSubmitButton: "Continue",
64 | codeLabel: "Code *",
65 | forgotPasswordConfirmLabel: "New Password *",
66 |
67 | /**
68 | * Errors
69 | */
70 | errorPasswordsDoNotMatch: "Passwords do not match",
71 | errorBadInputFormat: "Bad input format",
72 | errorPasswordTooShort: "Password must be at least 8 characters long",
73 | errorUserAlreadyExists: "An account with that email already exists",
74 | errorUserDoesNotExist: "Incorrect email or password",
75 | errorRequestLimitExceeded: "Password recently changed, please try again later",
76 | errorNoAccountFound: "No account found",
77 | errorWrongVerificationCode: "Incorrect verification code"
78 | }
79 |
--------------------------------------------------------------------------------
/src/useEasybase.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import EasybaseContext from "./EasybaseContext";
3 |
4 | const useEasybase = () => {
5 | const easybase = useContext(EasybaseContext);
6 | return easybase;
7 | }
8 |
9 | export default useEasybase;
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "module": "esnext",
5 | "lib": [
6 | "dom",
7 | "esnext"
8 | ],
9 | "moduleResolution": "node",
10 | "jsx": "react",
11 | "sourceMap": true,
12 | "declaration": true,
13 | "esModuleInterop": true,
14 | "noImplicitReturns": true,
15 | "noImplicitThis": true,
16 | "noImplicitAny": true,
17 | "strictNullChecks": true,
18 | "suppressImplicitAnyIndexErrors": true,
19 | "noUnusedLocals": false,
20 | "noUnusedParameters": false,
21 | "allowSyntheticDefaultImports": true,
22 | "target": "es5",
23 | "allowJs": true,
24 | "skipLibCheck": true,
25 | "strict": true,
26 | "forceConsistentCasingInFileNames": true,
27 | "resolveJsonModule": true,
28 | "isolatedModules": true,
29 | "noEmit": true
30 | },
31 | "include": [
32 | "src"
33 | ],
34 | "exclude": [
35 | "node_modules",
36 | "dist",
37 | "example"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "entryPoints": [
3 | "./src/"
4 | ],
5 | "out": "docs-api",
6 | "excludeProtected": true,
7 | "exclude": [
8 | "./src/EasybaseProvider.tsx",
9 | "./src/index.tsx",
10 | "./src/cache.tsx",
11 | "./src/types/typings.d.ts",
12 | "./src/types/react-app-env.d.ts",
13 | "./src/types/decs.d.ts",
14 | "./src/ui/themes",
15 | "./src/ui/Auth/components",
16 | "./src/ui/Auth/pages",
17 | "./src/ui/utils.tsx",
18 | "./src/ui/Auth/Auth.tsx",
19 | "./src/ui/NativeAuth/pages",
20 | "./src/ui/NativeAuth/components.tsx",
21 | "./src/ui/NativeAuth/NativeAuth.tsx"
22 | ],
23 | "externalPattern": [
24 | "./src/types/*.ts",
25 | "./src/types/*.tsx"
26 | ],
27 | "gitRevision": "dev",
28 | "excludePrivate": true,
29 | "excludeExternals": false,
30 | "categorizeByGroup": true,
31 | "readme": "none"
32 | }
--------------------------------------------------------------------------------