diff --git a/android/app/build.gradle b/android/app/build.gradle
index f7fa3a9..1af43bb 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -311,3 +311,9 @@ def isNewArchitectureEnabled() {
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
+
+project.ext.vectoricons = [
+ iconFontNames: [ 'MaterialIcons.ttf', 'MaterialCommunityIcons.ttf', 'FontAwesome.ttf', 'Ionicons.ttf' ] // Name of the font files you want to copy
+]
+
+apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
\ No newline at end of file
diff --git a/index.web.tsx b/index.web.tsx
index 440b36a..9ad4e24 100644
--- a/index.web.tsx
+++ b/index.web.tsx
@@ -3,13 +3,87 @@ import {AppRegistry} from 'react-native';
import App from './src/App';
-//AppRegistry.registerComponent("App", () => App)
+// @ts-ignore
+import outfitFont from './web/public/fonts/Outfit.ttf';
+// @ts-ignore
+import FontAwesome from 'react-native-vector-icons/Fonts/FontAwesome.ttf';
+// @ts-ignore
+import MaterialIcons from 'react-native-vector-icons/Fonts/MaterialIcons.ttf';
+// @ts-ignore
+import Ionicons from 'react-native-vector-icons/Fonts/Ionicons.ttf';
+// @ts-ignore
+import MaterialCommunityIcons from 'react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf';
-/*
-AppRegistry.runApplication("App", {
- initialProps: {},
- rootTag: document.getElementById("root"),
-})*/
+const iconFontStyles = `@font-face {
+ src: url(${MaterialCommunityIcons});
+ font-family: MaterialCommunityIcons;
+}
+@font-face {
+ src: url(${FontAwesome});
+ font-family: FontAwesome;
+}
+@font-face {
+ src: url(${MaterialIcons});
+ font-family: MaterialIcons;
+}
+@font-face {
+ src: url(${Ionicons});
+ font-family: Ionicons;
+}
+@font-face {
+ font-family: 'Outfit-Thin';
+ src: url(${outfitFont});
+ font-weight: 100;
+}
+@font-face {
+ font-family: 'Outfit-ExtraLight';
+ src: url(${outfitFont});
+ font-weight: 200;
+}
+@font-face {
+ font-family: 'Outfit-Light';
+ src: url(${outfitFont});
+ font-weight: 300;
+}
+@font-face {
+ font-family: 'Outfit-Regular';
+ src: url(${outfitFont});
+ font-weight: 400;
+}
+@font-face {
+ font-family: 'Outfit-Medium';
+ src: url(${outfitFont});
+ font-weight: 500;
+}
+@font-face {
+ font-family: 'Outfit-SemiBold';
+ src: url(${outfitFont});
+ font-weight: 600;
+}
+@font-face {
+ font-family: 'Outfit-Bold';
+ src: url(${outfitFont});
+ font-weight: 700;
+}
+@font-face {
+ font-family: 'Outfit-ExtraBold';
+ src: url(${outfitFont});
+ font-weight: 800;
+}
+@font-face {
+ font-family: 'Outfit-Black';
+ src: url(${outfitFont});
+ font-weight: 900;
+}
+
+`;
+
+const style = document.createElement('style');
+style.type = 'text/css';
+
+style.appendChild(document.createTextNode(iconFontStyles));
+
+document.head.appendChild(style);
const root = createRoot(document.getElementById('root') as HTMLElement);
root.render();
diff --git a/package-lock.json b/package-lock.json
index 53aed4f..931fc8e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,12 +8,14 @@
"name": "ClickAndJoinApp",
"version": "0.0.1",
"dependencies": {
+ "@react-native-masked-view/masked-view": "^0.2.8",
"@react-navigation/bottom-tabs": "^6.5.2",
"@react-navigation/native": "^6.1.1",
"@react-navigation/native-stack": "^6.9.7",
"@react-spring/native": "^9.6.1",
"@react-spring/web": "^9.6.1",
"@reduxjs/toolkit": "^1.9.1",
+ "@types/react-native-vector-icons": "^6.4.12",
"babel-preset-es2015": "^6.24.1",
"babel-preset-esnext": "^1.1.3",
"babel-preset-react": "^6.24.1",
@@ -26,8 +28,12 @@
"react-native-reanimated": "^2.13.0",
"react-native-safe-area-context": "^4.4.1",
"react-native-screens": "^3.18.2",
+ "react-native-svg": "^13.6.0",
+ "react-native-user-agent": "^2.3.1",
+ "react-native-vector-icons": "^9.2.0",
"react-native-web": "^0.18.10",
- "react-redux": "^8.0.5"
+ "react-redux": "^8.0.5",
+ "react-string-replace": "^1.1.0"
},
"devDependencies": {
"@babel/core": "^7.12.9",
@@ -4964,6 +4970,15 @@
"integrity": "sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==",
"dev": true
},
+ "node_modules/@react-native-masked-view/masked-view": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.8.tgz",
+ "integrity": "sha512-+1holBPDF1yi/y0uc1WB6lA5tSNHhM7PpTMapT3ypvSnKQ9+C6sy/zfjxNxRA/llBQ1Ci6f94EaK56UCKs5lTA==",
+ "peerDependencies": {
+ "react": ">=16",
+ "react-native": ">=0.57"
+ }
+ },
"node_modules/@react-native/assets": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz",
@@ -5906,11 +5921,19 @@
"version": "0.70.7",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.7.tgz",
"integrity": "sha512-hBzeUWwk8sfj3vDfwEXb4hbjWjl0jb5CvWlu2gLrOUJyFHVzJ+x6Y9ilO2eVtJW7l5QmmNLILE1PkVfKRkqYuQ==",
- "peer": true,
"dependencies": {
"@types/react": "*"
}
},
+ "node_modules/@types/react-native-vector-icons": {
+ "version": "6.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.12.tgz",
+ "integrity": "sha512-gSXtv3NMOsRwSXJ/gvGebm7CNjHbkeFKCse9h/Pvi+x2yjCLOkJR1FBfec06DhaFJpsK7Y8WRQpwOS0eLqx5Rg==",
+ "dependencies": {
+ "@types/react": "*",
+ "@types/react-native": "*"
+ }
+ },
"node_modules/@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
@@ -8154,8 +8177,7 @@
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "peer": true
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
@@ -8955,7 +8977,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
- "peer": true,
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
@@ -8971,7 +8992,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
- "peer": true,
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
@@ -8984,7 +9004,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
- "peer": true,
"engines": {
"node": ">= 6"
},
@@ -9304,7 +9323,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "peer": true,
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
@@ -9329,8 +9347,7 @@
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
- ],
- "peer": true
+ ]
},
"node_modules/domexception": {
"version": "2.0.1",
@@ -9357,7 +9374,6 @@
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "peer": true,
"dependencies": {
"domelementtype": "^2.3.0"
},
@@ -9372,7 +9388,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
- "peer": true,
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
@@ -9463,7 +9478,6 @@
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
"integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
- "peer": true,
"engines": {
"node": ">=0.12"
},
@@ -14123,9 +14137,9 @@
"dev": true
},
"node_modules/json5": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
- "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz",
+ "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==",
"bin": {
"json5": "lib/cli.js"
},
@@ -14481,8 +14495,7 @@
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
- "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
- "peer": true
+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
},
"node_modules/media-typer": {
"version": "0.3.0",
@@ -15730,7 +15743,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "peer": true,
"dependencies": {
"boolbase": "^1.0.0"
},
@@ -16849,7 +16861,6 @@
"version": "13.6.0",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.6.0.tgz",
"integrity": "sha512-1wjHCMJ8siyZbDZ0MX5wM+Jr7YOkb6GADn4/Z+/u1UwJX8WfjarypxDF3UO1ugMHa+7qor39oY+URMcrgPpiww==",
- "peer": true,
"dependencies": {
"css-select": "^5.1.0",
"css-tree": "^1.1.3"
@@ -16859,6 +16870,116 @@
"react-native": "*"
}
},
+ "node_modules/react-native-user-agent": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/react-native-user-agent/-/react-native-user-agent-2.3.1.tgz",
+ "integrity": "sha512-AIFr1VgJHwgWmMwCOmIGxuBeAaADlouXKc10UyR4fzWneUbt5uIJIoRu2oExlfCtiT8IyCp106khDD5vx7RUjw==",
+ "peerDependencies": {
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-vector-icons": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz",
+ "integrity": "sha512-wKYLaFuQST/chH3AJRjmOLoLy3JEs1JR6zMNgTaemFpNoXs0ztRnTxcxFD9xhX7cJe1/zoN5BpQYe7kL0m5yyA==",
+ "dependencies": {
+ "prop-types": "^15.7.2",
+ "yargs": "^16.1.1"
+ },
+ "bin": {
+ "fa5-upgrade": "bin/fa5-upgrade.sh",
+ "generate-icon": "bin/generate-icon.js"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/react-native-vector-icons/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/react-native-web": {
"version": "0.18.10",
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.18.10.tgz",
@@ -16958,6 +17079,14 @@
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-string-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/react-string-replace/-/react-string-replace-1.1.0.tgz",
+ "integrity": "sha512-N6RalSDFGbOHs0IJi1H611WbZsvk3ZT47Jl2JEXFbiS3kTwsdCYij70Keo/tWtLy7sfhDsYm7CwNM/WmjXIaMw==",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
"node_modules/react-test-renderer": {
"version": "18.1.0",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.1.0.tgz",
@@ -24109,6 +24238,12 @@
"integrity": "sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==",
"dev": true
},
+ "@react-native-masked-view/masked-view": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.8.tgz",
+ "integrity": "sha512-+1holBPDF1yi/y0uc1WB6lA5tSNHhM7PpTMapT3ypvSnKQ9+C6sy/zfjxNxRA/llBQ1Ci6f94EaK56UCKs5lTA==",
+ "requires": {}
+ },
"@react-native/assets": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz",
@@ -24892,11 +25027,19 @@
"version": "0.70.7",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.7.tgz",
"integrity": "sha512-hBzeUWwk8sfj3vDfwEXb4hbjWjl0jb5CvWlu2gLrOUJyFHVzJ+x6Y9ilO2eVtJW7l5QmmNLILE1PkVfKRkqYuQ==",
- "peer": true,
"requires": {
"@types/react": "*"
}
},
+ "@types/react-native-vector-icons": {
+ "version": "6.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.12.tgz",
+ "integrity": "sha512-gSXtv3NMOsRwSXJ/gvGebm7CNjHbkeFKCse9h/Pvi+x2yjCLOkJR1FBfec06DhaFJpsK7Y8WRQpwOS0eLqx5Rg==",
+ "requires": {
+ "@types/react": "*",
+ "@types/react-native": "*"
+ }
+ },
"@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
@@ -26820,8 +26963,7 @@
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "peer": true
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"brace-expansion": {
"version": "1.1.11",
@@ -27432,7 +27574,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
- "peer": true,
"requires": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
@@ -27445,7 +27586,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
- "peer": true,
"requires": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
@@ -27454,8 +27594,7 @@
"css-what": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
- "peer": true
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
},
"cssom": {
"version": "0.4.4",
@@ -27692,7 +27831,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "peer": true,
"requires": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
@@ -27708,8 +27846,7 @@
"domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
- "peer": true
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
},
"domexception": {
"version": "2.0.1",
@@ -27732,7 +27869,6 @@
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "peer": true,
"requires": {
"domelementtype": "^2.3.0"
}
@@ -27741,7 +27877,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
- "peer": true,
"requires": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
@@ -27810,8 +27945,7 @@
"entities": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
- "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
- "peer": true
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
},
"envinfo": {
"version": "7.8.1",
@@ -31309,9 +31443,9 @@
"dev": true
},
"json5": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
- "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA=="
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz",
+ "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ=="
},
"jsonfile": {
"version": "4.0.0",
@@ -31591,8 +31725,7 @@
"mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
- "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
- "peer": true
+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
},
"media-typer": {
"version": "0.3.0",
@@ -32625,7 +32758,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "peer": true,
"requires": {
"boolbase": "^1.0.0"
}
@@ -33473,12 +33605,93 @@
"version": "13.6.0",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.6.0.tgz",
"integrity": "sha512-1wjHCMJ8siyZbDZ0MX5wM+Jr7YOkb6GADn4/Z+/u1UwJX8WfjarypxDF3UO1ugMHa+7qor39oY+URMcrgPpiww==",
- "peer": true,
"requires": {
"css-select": "^5.1.0",
"css-tree": "^1.1.3"
}
},
+ "react-native-user-agent": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/react-native-user-agent/-/react-native-user-agent-2.3.1.tgz",
+ "integrity": "sha512-AIFr1VgJHwgWmMwCOmIGxuBeAaADlouXKc10UyR4fzWneUbt5uIJIoRu2oExlfCtiT8IyCp106khDD5vx7RUjw==",
+ "requires": {}
+ },
+ "react-native-vector-icons": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz",
+ "integrity": "sha512-wKYLaFuQST/chH3AJRjmOLoLy3JEs1JR6zMNgTaemFpNoXs0ztRnTxcxFD9xhX7cJe1/zoN5BpQYe7kL0m5yyA==",
+ "requires": {
+ "prop-types": "^15.7.2",
+ "yargs": "^16.1.1"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ },
+ "yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
+ }
+ }
+ },
"react-native-web": {
"version": "0.18.10",
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.18.10.tgz",
@@ -33537,6 +33750,11 @@
"react-is": "^16.12.0 || ^17.0.0 || ^18.0.0"
}
},
+ "react-string-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/react-string-replace/-/react-string-replace-1.1.0.tgz",
+ "integrity": "sha512-N6RalSDFGbOHs0IJi1H611WbZsvk3ZT47Jl2JEXFbiS3kTwsdCYij70Keo/tWtLy7sfhDsYm7CwNM/WmjXIaMw=="
+ },
"react-test-renderer": {
"version": "18.1.0",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.1.0.tgz",
diff --git a/package.json b/package.json
index b68f8c1..0bc6348 100644
--- a/package.json
+++ b/package.json
@@ -12,12 +12,14 @@
"lint": "eslint ."
},
"dependencies": {
+ "@react-native-masked-view/masked-view": "^0.2.8",
"@react-navigation/bottom-tabs": "^6.5.2",
"@react-navigation/native": "^6.1.1",
"@react-navigation/native-stack": "^6.9.7",
"@react-spring/native": "^9.6.1",
"@react-spring/web": "^9.6.1",
"@reduxjs/toolkit": "^1.9.1",
+ "@types/react-native-vector-icons": "^6.4.12",
"babel-preset-es2015": "^6.24.1",
"babel-preset-esnext": "^1.1.3",
"babel-preset-react": "^6.24.1",
@@ -30,8 +32,12 @@
"react-native-reanimated": "^2.13.0",
"react-native-safe-area-context": "^4.4.1",
"react-native-screens": "^3.18.2",
+ "react-native-svg": "^13.6.0",
+ "react-native-user-agent": "^2.3.1",
+ "react-native-vector-icons": "^9.2.0",
"react-native-web": "^0.18.10",
- "react-redux": "^8.0.5"
+ "react-redux": "^8.0.5",
+ "react-string-replace": "^1.1.0"
},
"devDependencies": {
"@babel/core": "^7.12.9",
@@ -59,7 +65,7 @@
"webpack-dev-server": "^4.11.1"
},
"jest": {
- "preset": "react-native",
+ "preset": "react-native-web",
"transform": {
"^.+\\.(js)$": "/node_modules/babel-jest",
"\\.(ts|tsx)$": "ts-jest"
diff --git a/src/App.tsx b/src/App.tsx
index a2a366e..a0ad896 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -6,7 +6,7 @@ import {NavigationContainer} from '@react-navigation/native';
import {getBackgroundColor, theme, ThemeSwitcher} from '@caj/configs/colors';
import StatusBar from './StatusBar';
-import Navigation, {linking} from './Navigation';
+import Navigation, {linking} from './caj/Navigation';
import {Provider, useSelector} from 'react-redux';
import {RootState, store} from '@caj/redux/store';
@@ -44,8 +44,8 @@ const OtherProviders = () => {
};
return (
-
-
+
+
diff --git a/src/appStart/StartHelper.tsx b/src/appStart/StartHelper.tsx
index acb37fa..c3d184e 100644
--- a/src/appStart/StartHelper.tsx
+++ b/src/appStart/StartHelper.tsx
@@ -23,7 +23,7 @@ function onAppStart() {
console.log('finish');
setTimeout(() => {
store.dispatch(appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING));
- }, 500);
+ }, 250);
//store.dispatch(actions.loadPreferences(appVar));
});
}
diff --git a/src/Navigation.tsx b/src/caj/Navigation copy.tsx
similarity index 85%
rename from src/Navigation.tsx
rename to src/caj/Navigation copy.tsx
index 8e68491..1af1c1a 100644
--- a/src/Navigation.tsx
+++ b/src/caj/Navigation copy.tsx
@@ -34,25 +34,24 @@ const styles = StyleSheet.create({
});
export const linking: LinkingOptions<{
- Auth: string;
+ Maps: string;
Home: string;
- Table: string;
- Loading: string;
+ Chat: string;
+ Settings: string;
}> = {
prefixes: ['http://'],
config: {
- initialRouteName: 'Loading',
screens: {
- Auth: 'auth',
- Home: 'home',
- Table: 'table',
- Loading: 'loading',
+ Maps: 'mapss',
+ Home: 'account',
+ Chat: 'chats',
+ Settings: 'settingss',
},
},
};
export type HomeStackNavigatorParamList = {
- Home: undefined;
+ Account: undefined;
Maps: undefined;
Chat: undefined;
Test: undefined;
@@ -100,12 +99,21 @@ function HomeScreen() {
);
}
+export type SettingsStackNavigatorParamList = {
+ Chat: undefined;
+ Settings: undefined;
+};
+
+export type SettingsScreenNavigationProp =
+ NativeStackNavigationProp;
+
function SettingsScreen() {
- const navigation = useNavigation();
+ const navigation = useNavigation();
+ const navigation2 = useNavigation();
return (
Settings screen
-
+
);
}
diff --git a/src/caj/Navigation.tsx b/src/caj/Navigation.tsx
new file mode 100644
index 0000000..44b23ec
--- /dev/null
+++ b/src/caj/Navigation.tsx
@@ -0,0 +1,126 @@
+import React, {useState, useEffect} from 'react';
+
+import {StyleSheet, Appearance, Dimensions} from 'react-native';
+import {SafeAreaProvider, SafeAreaView} from 'react-native-safe-area-context';
+
+import {useSelector, useDispatch} from 'react-redux';
+import {RootState} from '@caj/redux/store';
+import {appVarActions} from '@caj/configs/appVarReducer';
+
+import imgSrc from '@caj/img/maimg.png';
+import {placeholder} from '@caj/lang/default';
+import {getBackgroundColor} from '@caj/configs/colors';
+import {saveVarChanges} from '@caj/helper/appData';
+
+import {Box, Input, VStack, Center, Avatar, Text, Button} from 'native-base';
+import {View} from 'react-native';
+
+import {
+ LinkingOptions,
+ NavigationContainer,
+ NavigatorScreenParams,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ NativeStackNavigationProp,
+} from '@react-navigation/native-stack';
+import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
+import AccountTab, {
+ AccountStackNavigatorParamList,
+ AccountTabName,
+} from './tabs/main/AccountTab';
+import CalendarTab, {
+ CalendarStackNavigatorParamList,
+} from './tabs/main/CalendarTab';
+import MapsTab, {MapsStackNavigatorParamList} from './tabs/main/MapsTab';
+import ChatTab, {ChatStackNavigatorParamList} from './tabs/main/ChatTab';
+import {
+ FadeInView,
+ SlideFromLeftView,
+ SlideFromRightView,
+} from './helper/animations';
+import {
+ LoginStackNavigatorParamList,
+ RegisterScreenAnim,
+} from './components/NotLoggedIn';
+
+const styles = StyleSheet.create({
+ container: {
+ height: '100%',
+ flex: 1,
+ },
+});
+
+export const linking: LinkingOptions<{}> = {
+ prefixes: ['http://'],
+};
+
+export type RootStackNavigatorParamList = {
+ Home: NavigatorScreenParams;
+ Register: NavigatorScreenParams;
+};
+
+export type RootScreenNavigationProp =
+ NativeStackNavigationProp;
+
+export default function Navigation() {
+ return (
+
+
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator();
+
+export type HomeStackNavigatorParamList = {
+ Account: NavigatorScreenParams;
+ Calendar: NavigatorScreenParams;
+ Maps: NavigatorScreenParams;
+ Chat: NavigatorScreenParams;
+};
+
+function AccountTabAnim(props: any) {
+ return (
+
+
+
+ );
+}
+
+function CalendarTabAnim(props: any) {
+ return (
+
+
+
+ );
+}
+function MapsTabAnim(props: any) {
+ return (
+
+
+
+ );
+}
+function ChatTabAnim(props: any) {
+ return (
+
+
+
+ );
+}
+
+function HomeStack() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
diff --git a/src/caj/components/AccountInfoBanner.tsx b/src/caj/components/AccountInfoBanner.tsx
new file mode 100644
index 0000000..0f3532d
--- /dev/null
+++ b/src/caj/components/AccountInfoBanner.tsx
@@ -0,0 +1,5 @@
+import {Center} from 'native-base';
+
+export default function AccountInfoBanner() {
+ return Account;
+}
diff --git a/src/caj/components/ConfirmationCodeField.tsx b/src/caj/components/ConfirmationCodeField.tsx
new file mode 100644
index 0000000..cf7d2aa
--- /dev/null
+++ b/src/caj/components/ConfirmationCodeField.tsx
@@ -0,0 +1,146 @@
+import {Box, Center, HStack, Input, Pressable, Text} from 'native-base';
+import {useEffect, useRef, useState} from 'react';
+import {
+ TextInput,
+ NativeSyntheticEvent,
+ TextInputChangeEventData,
+} from 'react-native';
+
+function RenderCell(props: {
+ index: number;
+ text: string | number;
+ maxIndex: number;
+ isFocus: boolean;
+ disabled: boolean;
+ rest: any;
+}) {
+ const display: string = props.text ? props.text.toString() : '';
+
+ const [toggle, setToggle] = useState(true);
+
+ useEffect(() => {
+ if (props.isFocus === false) return;
+
+ let timer = setInterval(() => {
+ setToggle(!toggle);
+ }, 500);
+ return () => {
+ clearInterval(timer);
+ };
+ });
+
+ return (
+
+
+
+ {props.isFocus ? (toggle ? '|' : '') : display}
+
+
+
+ );
+}
+
+function ConfirmationCodeField(props: {
+ cellCount: number;
+ charType: 'number' | 'letter';
+ onFinish?: Function;
+ onChange?: Function;
+ disabled?: boolean;
+ rest?: any;
+}) {
+ const [values, setValues] = useState({input: ''});
+ const [isFocus, setFocus] = useState(false);
+
+ const inputRef = useRef(null);
+
+ return (
+
+ setFocus(true)}
+ onBlur={() => setFocus(false)}
+ editable={!props.disabled}
+ onChange={(event: NativeSyntheticEvent) => {
+ let text = event.nativeEvent.text;
+ let vals = {...values};
+
+ if (props.charType === 'number')
+ vals.input = text.replace(/[^0-9]/g, '');
+ else if (props.charType === 'letter') vals.input = text;
+
+ vals.input = vals.input.slice(0, props.cellCount);
+
+ setValues(vals);
+
+ if (props.onChange) props.onChange(vals.input);
+
+ if (vals.input.length === props.cellCount) {
+ if (inputRef.current !== null) {
+ inputRef.current.blur();
+ if (props.onFinish) props.onFinish(vals.input);
+ }
+ }
+ }}
+ style={{
+ position: 'absolute',
+ opacity: 0,
+ width: 1,
+ height: 1,
+
+ padding: 0,
+ margin: 0,
+ }}
+ />
+
+ {(() => {
+ let cells = [];
+ for (let i = 0; i < props.cellCount; i++) {
+ cells.push(
+ {
+ let vals = {...values};
+ vals.input = vals.input.slice(0, i);
+ setValues(vals);
+
+ if (props.onChange) props.onChange(vals.input);
+
+ if (inputRef.current !== null) {
+ inputRef.current.focus();
+ }
+ }
+ : undefined,
+ }}
+ />,
+ );
+ }
+ return cells;
+ })()}
+
+
+ );
+}
+
+export default ConfirmationCodeField;
diff --git a/src/caj/components/NameDisplay.tsx b/src/caj/components/NameDisplay.tsx
new file mode 100644
index 0000000..fd0bdcb
--- /dev/null
+++ b/src/caj/components/NameDisplay.tsx
@@ -0,0 +1,30 @@
+import {AccountName, Username} from '@caj/helper/types';
+import {RootState} from '@caj/redux/store';
+import {HStack, Text} from 'native-base';
+import {useSelector} from 'react-redux';
+
+export default function NameDisplay(props: {
+ UserName: Username;
+ AccountName: AccountName;
+ fontSize?: number;
+}) {
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+ const fontSize = props.fontSize || 15;
+ const lineHeight = fontSize * 1.25;
+
+ return (
+
+
+ {props.UserName !== '' ? props.UserName : '----'}
+
+
+ {props.AccountName !== '' ? props.AccountName : '----'}
+
+
+ );
+}
diff --git a/src/caj/components/NotLoggedIn.tsx b/src/caj/components/NotLoggedIn.tsx
new file mode 100644
index 0000000..1db50f1
--- /dev/null
+++ b/src/caj/components/NotLoggedIn.tsx
@@ -0,0 +1,960 @@
+import {RegisterProcess, ThemeMode} from '@caj/configs/appVar';
+import {appVarActions} from '@caj/configs/appVarReducer';
+import {defaultHeaderStyle} from '@caj/configs/colors';
+import {SlideFromLeftView} from '@caj/helper/animations';
+import {saveVarChanges} from '@caj/helper/appData';
+import {apiBackendRequest, makeRequest} from '@caj/helper/request';
+import {
+ accountNameOptions,
+ EMail,
+ emailOptions,
+ passwordOptions,
+ userNameOptions,
+ XToken,
+} from '@caj/helper/types';
+import {RootScreenNavigationProp} from '@caj/Navigation';
+import {RootState, store} from '@caj/redux/store';
+import {useNavigation} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ NativeStackNavigationProp,
+} from '@react-navigation/native-stack';
+import {
+ Box,
+ Button,
+ Center,
+ Container,
+ FormControl,
+ Heading,
+ HStack,
+ Icon,
+ IconButton,
+ Input,
+ Pressable,
+ ScrollView,
+ Spinner,
+ Text,
+ useColorModeValue,
+ useTheme,
+ useToast,
+ VStack,
+ WarningOutlineIcon,
+} from 'native-base';
+
+import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
+
+import {baseFontSize} from 'native-base/lib/typescript/theme/tools';
+import {useEffect, useRef, useState} from 'react';
+import {useDispatch, useSelector} from 'react-redux';
+import reactStringReplace from 'react-string-replace';
+import ConfirmationCodeField from './ConfirmationCodeField';
+import NameDisplay from './NameDisplay';
+import showToast from './Toast';
+import {NativeSyntheticEvent, TextInputFocusEventData} from 'react-native';
+
+const validateEmail = (email: EMail) => {
+ return emailOptions.isAllowed(email);
+};
+
+export default function NotLoggedIn() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+
+ const toast = useToast();
+
+ const navigation = useNavigation();
+
+ return (
+
+ {lang.appName}
+ {lang.appNameDesc}
+
+
+
+
+
+ );
+}
+
+export function RegisterScreenAnim(props: any) {
+ return (
+
+
+
+ );
+}
+
+export type LoginStackNavigatorParamList = {
+ RegStepOne: undefined;
+ RegStepTwo: undefined;
+ RegStepFinal: undefined;
+};
+
+const LoginStack = createNativeStackNavigator();
+
+export type LoginScreenNavigationProp =
+ NativeStackNavigationProp;
+
+function RegisterScreen() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+
+ return (
+
+
+
+
+
+ );
+}
+
+function Agreement() {
+ const toast = useToast();
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+
+ const textColor = useColorModeValue('blue.700', 'cyan.400');
+
+ let replacedText = reactStringReplace(
+ lang.account.registration.info,
+ '${TermsOfUse}',
+ (match, i) => (
+ {
+ showToast(toast, {
+ title: lang.account.registration.termsOfUse,
+ variant: 'solid',
+ status: 'info',
+ description: undefined,
+ isClosable: true,
+ rest: {colorScheme: 'primary'},
+ });
+ }}>
+ {lang.account.registration.termsOfUse}
+
+ ),
+ );
+
+ replacedText = reactStringReplace(
+ replacedText,
+ '${privacyPolicy}',
+ (match, i) => (
+ {
+ showToast(toast, {
+ title: lang.account.registration.privacyPolicy,
+ variant: 'solid',
+ status: 'info',
+ description: undefined,
+ isClosable: true,
+ rest: {colorScheme: 'primary'},
+ });
+ }}>
+ {lang.account.registration.privacyPolicy}
+
+ ),
+ );
+
+ return (
+
+ {replacedText}
+
+ );
+}
+
+function resendMail(email: EMail, toast: any): Promise {
+ return new Promise((resolve, reject) => {
+ makeRequest({
+ path: apiBackendRequest.REGISTER_RESEND_MAIL,
+
+ requestHeader: {},
+ request: {
+ Email: email,
+ },
+ //response: {
+ // XToken: undefined,
+ //},
+ })
+ .then(resp => {
+ console.log(1);
+ let token =
+ store.getState().appVariables.preferences.RegisterProcess.XToken;
+ if (token !== undefined /*resp.response.XToken !== undefined*/) {
+ showToast(toast, {
+ title: store.getState().appVariables.lang.info,
+ variant: 'solid',
+ status: 'info',
+ description:
+ store.getState().appVariables.lang.account.registration.stepTwo
+ .resend[2],
+ isClosable: true,
+ rest: {},
+ });
+
+ resolve(token);
+ } else {
+ reject(500);
+ showToast(toast, {
+ title: store.getState().appVariables.lang.error,
+ variant: 'solid',
+ status: 'error',
+ description: 'XToken is undefined',
+ isClosable: true,
+ rest: {},
+ });
+ }
+ })
+ .catch(resp => {
+ let text = 'unknown error ' + resp.status;
+ if (resp.status !== undefined) {
+ const _text =
+ store.getState().appVariables.lang.account.registration.stepTwo
+ .resendError[resp.status as number];
+ if (_text !== undefined) text = _text;
+ }
+
+ showToast(toast, {
+ title: store.getState().appVariables.lang.error,
+ variant: 'solid',
+ status: 'error',
+ description: text,
+ isClosable: true,
+ rest: {},
+ });
+
+ reject(resp.status);
+ });
+ });
+}
+
+function StepOne() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const regPro = useSelector(
+ (state: RootState) => state.appVariables.preferences.RegisterProcess,
+ );
+
+ const dispatch = useDispatch();
+ const toast = useToast();
+
+ const navigation = useNavigation();
+
+ const initNoErrors = {
+ wrongFormat: false,
+ alreadyExists: false,
+ noEntered: false,
+ unknown: undefined,
+ };
+
+ const [errors, setErrors] = useState(initNoErrors);
+
+ const isError =
+ errors.wrongFormat ||
+ errors.alreadyExists ||
+ errors.noEntered ||
+ errors.unknown !== undefined;
+
+ const errorText = () => {
+ if (errors.wrongFormat) {
+ return lang.account.registration.stepOne.addressInvalid;
+ }
+ if (errors.alreadyExists) {
+ return lang.account.registration.stepOne.addressExists;
+ }
+ if (errors.noEntered) {
+ return lang.account.registration.stepOne.noMailEntered;
+ }
+ if (errors.unknown !== undefined) {
+ return errors.unknown;
+ }
+ };
+
+ const [isLoading, setLoading] = useState(false);
+
+ const [values, setValues] = useState({email: regPro.EMail});
+
+ useEffect(() => {
+ if (regPro.isRegistering === 'stepTwo') {
+ setLoading(true);
+ setErrors(initNoErrors);
+
+ setTimeout(nextStep, 500);
+ } else if (regPro.isRegistering === 'stepFinal') {
+ setLoading(true);
+ setErrors(initNoErrors);
+
+ setTimeout(nextStep, 500);
+ }
+ }, []);
+
+ const nextStep = () => {
+ setLoading(true);
+ setErrors(initNoErrors);
+
+ makeRequest({
+ path: apiBackendRequest.REGISTER_STEP_1,
+ requestHeader: {},
+ request: {
+ Email: values.email,
+ },
+ response: {
+ XToken: undefined,
+ },
+ })
+ .then(resp => {
+ let rp = {...regPro};
+ rp.isRegistering = 'stepTwo';
+ rp.EMail = values.email;
+ rp.XToken = resp.response.XToken;
+
+ dispatch(appVarActions.setRegisterProcess(rp));
+ saveVarChanges();
+
+ showToast(toast, {
+ title: lang.account.registration.stepOne.success,
+ variant: 'solid',
+ status: 'success',
+ description: undefined,
+ isClosable: true,
+ rest: {colorScheme: 'primary'},
+ });
+
+ navigation.navigate('Register', {screen: 'RegStepTwo'});
+ setLoading(false);
+ })
+ .catch(resp => {
+ if (resp.status === 401 || resp.status === 204) {
+ if (regPro.XToken !== undefined) {
+ let rp = {...regPro};
+
+ if (resp.status === 401) {
+ rp.isRegistering = 'stepTwo';
+ rp.EMail = values.email;
+
+ dispatch(appVarActions.setRegisterProcess(rp));
+ saveVarChanges();
+
+ navigation.navigate('Register', {screen: 'RegStepTwo'});
+ } else if (resp.status === 204) {
+ rp.isRegistering = 'stepFinal';
+ rp.EMail = values.email;
+
+ dispatch(appVarActions.setRegisterProcess(rp));
+ saveVarChanges();
+
+ navigation.navigate('Register', {screen: 'RegStepFinal'});
+ }
+
+ setLoading(false);
+ } else {
+ resendMail(values.email, toast)
+ .then(() => {
+ setLoading(false);
+ })
+ .catch(() => {
+ setLoading(false);
+ });
+ }
+ } else {
+ showToast(toast, {
+ title: 'Error',
+ variant: 'solid',
+ status: 'error',
+ description: resp.status,
+ isClosable: true,
+ rest: {colorScheme: 'primary'},
+ });
+ setLoading(false);
+ }
+ });
+ };
+
+ return (
+
+
+
+
+ {lang.account.registration.stepOne.title}
+
+ {
+ const mail = text.replaceAll(' ', '');
+ setValues({email: mail});
+
+ if (errors.noEntered && mail !== '') {
+ let err = errors;
+ err.noEntered = false;
+ setErrors({...err});
+ }
+ if (errors.wrongFormat && validateEmail(mail)) {
+ let err = errors;
+ err.wrongFormat = false;
+ setErrors({...err});
+ }
+ if (errors.alreadyExists) {
+ let err = errors;
+ err.alreadyExists = false;
+ setErrors({...err});
+ }
+ }}
+ />
+
+ }>
+ {errorText()}
+
+
+
+
+
+
+
+
+ );
+}
+
+function StepTwo() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const regPro = useSelector(
+ (state: RootState) => state.appVariables.preferences.RegisterProcess,
+ );
+
+ const dispatch = useDispatch();
+
+ const cellCount = 6;
+ const {colors} = useTheme();
+
+ const toast = useToast();
+
+ const navigation = useNavigation();
+
+ const initNoErrors = {
+ noEntered: false,
+ };
+
+ const [errors, setErrors] = useState(initNoErrors);
+
+ const [isLoading, setLoading] = useState(false);
+
+ const [values, setValues] = useState({code: ''});
+
+ const headerText = () => {
+ return reactStringReplace(
+ lang.account.registration.stepTwo.title,
+ '${EMail}',
+ (match, i) => (
+
+ {regPro.EMail}
+
+ ),
+ );
+ };
+
+ const resendText = () => {
+ return reactStringReplace(
+ lang.account.registration.stepTwo.resend[0],
+ '${resend}',
+ (match, i) => (
+ {
+ setLoading(true);
+ resendMail(
+ store.getState().appVariables.preferences.RegisterProcess.EMail,
+ toast,
+ )
+ .then(() => {
+ setLoading(false);
+ })
+ .catch(() => {
+ setLoading(false);
+ });
+ }}>
+ {lang.account.registration.stepTwo.resend[1]}
+
+ ),
+ );
+ };
+
+ const validate = (text: string) => {
+ setLoading(true);
+ setTimeout(() => {
+ makeRequest({
+ path: apiBackendRequest.REGISTER_STEP_2,
+
+ requestHeader: {},
+ requestGET: {':verifyId': text, ':xToken': regPro.XToken || ''},
+ })
+ .then(resp => {
+ let rp = {...regPro};
+ rp.isRegistering = 'stepFinal';
+
+ dispatch(appVarActions.setRegisterProcess(rp));
+ saveVarChanges();
+
+ showToast(toast, {
+ title: lang.account.registration.stepTwo.success,
+ variant: 'solid',
+ status: 'success',
+ description: undefined,
+ isClosable: true,
+ rest: {colorScheme: 'primary'},
+ });
+
+ navigation.navigate('Register', {screen: 'RegStepFinal'});
+ setLoading(false);
+ })
+ .catch(resp => {
+ let text = 'unknown error ' + resp.status;
+ if (resp.status !== undefined) {
+ const _text =
+ lang.account.registration.stepTwo.verificationError[
+ resp.status as number
+ ];
+ if (_text !== undefined) text = _text;
+ }
+
+ showToast(toast, {
+ title: lang.error,
+ variant: 'solid',
+ status: 'error',
+ description: text,
+ isClosable: true,
+ rest: {},
+ });
+
+ setLoading(false);
+ if (resp.status === 422) {
+ let rp = {...regPro};
+ rp.isRegistering = false;
+
+ dispatch(appVarActions.setRegisterProcess(rp));
+ saveVarChanges();
+ navigation.navigate('Register', {screen: 'RegStepOne'});
+ }
+ });
+ }, 500);
+ };
+
+ return (
+
+
+
+ {headerText()}
+
+
+ {
+ setValues({code: text});
+ setErrors(initNoErrors);
+ }}
+ onFinish={(text: string) => validate(text)}
+ />
+
+ }>
+ {lang.account.registration.stepTwo.noCodeEntered}
+
+
+
+
+
+
+
+
+
+
+ {resendText()}
+
+
+
+
+ );
+}
+function StepFinal() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const regPro = useSelector(
+ (state: RootState) => state.appVariables.preferences.RegisterProcess,
+ );
+
+ const dispatch = useDispatch();
+
+ const {colors} = useTheme();
+
+ const toast = useToast();
+
+ const navigation = useNavigation();
+
+ const [isLoading, setLoading] = useState(false);
+ const [showPassword, setShowPassword] = useState(false);
+
+ interface inputElementType {
+ label: string;
+ input: string;
+ autoCapitalize: 'none' | 'words';
+ errorIndex: 'none' | string;
+ errorTextObject: any;
+ isPassword: boolean | 'passwordRepeat';
+ minLength: number;
+ maxLength: number;
+ onTextChange: any;
+ textChangeTimeout: number;
+ isAllowed: any;
+ }
+
+ const accountNameRef = useRef(setTimeout(() => {}));
+ const accountNameFetchRef = useRef(setTimeout(() => {}));
+
+ const accountName = {
+ label: lang.account.registration.stepFinal.accountName,
+ input: '',
+ errorIndex: 'none',
+ errorTextObject: lang.account.registration.stepFinal.accountNameError,
+ minLength: accountNameOptions.minLength,
+ maxLength: accountNameOptions.maxLength,
+ isAllowed: accountNameOptions.isAllowed,
+ isPassword: false,
+ onTextChange: (e: NativeSyntheticEvent) => {
+ let text = e.nativeEvent.text;
+ let self = accountName;
+
+ clearTimeout(accountNameRef.current);
+
+ let obj = {...valuesAccountName};
+ accountNameRef.current = setTimeout(() => {
+ obj.input = text;
+
+ if (text.length < self.minLength) obj.errorIndex = 'tooShort';
+ else if (text.length > self.maxLength) obj.errorIndex = 'tooLong';
+ else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid';
+ else obj.errorIndex = 'none';
+
+ setValuesAccountName(obj);
+ }, 50);
+
+ clearTimeout(accountNameFetchRef.current);
+ accountNameFetchRef.current = setTimeout(() => {
+ console.log(obj);
+
+ if (obj.errorIndex === 'none') {
+ makeRequest({
+ path: apiBackendRequest.REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK,
+ request: {AccountName: obj.input},
+ })
+ .then(resp => {
+ console.log('OK');
+ })
+ .catch(resp => {
+ if (resp.status !== undefined) {
+ obj.errorIndex = resp.status;
+ setValuesAccountName(obj);
+ }
+ });
+ }
+ }, 750);
+ },
+ } as inputElementType;
+ const [valuesAccountName, setValuesAccountName] = useState(accountName);
+
+ const userNameRef = useRef(setTimeout(() => {}));
+
+ const userName = {
+ label: lang.account.registration.stepFinal.userName,
+ input: '',
+ errorIndex: 'none',
+ errorTextObject: lang.account.registration.stepFinal.userNameError,
+ minLength: userNameOptions.minLength,
+ maxLength: userNameOptions.maxLength,
+ isAllowed: userNameOptions.isAllowed,
+ isPassword: false,
+ onTextChange: (e: NativeSyntheticEvent) => {
+ let text = e.nativeEvent.text;
+ let self = userName;
+
+ clearTimeout(userNameRef.current);
+
+ userNameRef.current = setTimeout(() => {
+ let obj = {...valuesUserName};
+ obj.input = text;
+
+ if (text.length < self.minLength) obj.errorIndex = 'tooShort';
+ else if (text.length > self.maxLength) obj.errorIndex = 'tooLong';
+ else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid';
+ else obj.errorIndex = 'none';
+
+ setValuesUserName(obj);
+ }, 50);
+ },
+ } as inputElementType;
+ const [valuesUserName, setValuesUserName] = useState(userName);
+
+ const passwordRef = useRef(setTimeout(() => {}));
+
+ const password = {
+ label: lang.account.registration.stepFinal.password,
+ input: '',
+ errorIndex: 'none',
+ errorTextObject: lang.account.registration.stepFinal.passwordError,
+ minLength: passwordOptions.minLength,
+ maxLength: passwordOptions.maxLength,
+ isAllowed: passwordOptions.isAllowed,
+ isPassword: true,
+ onTextChange: (e: NativeSyntheticEvent) => {
+ let text = e.nativeEvent.text;
+ let self = password;
+
+ clearTimeout(passwordRef.current);
+
+ passwordRef.current = setTimeout(() => {
+ let obj = {...valuesPassword};
+ obj.input = text;
+
+ if (text.length < self.minLength) obj.errorIndex = 'tooShort';
+ else if (text.length > self.maxLength) obj.errorIndex = 'tooLong';
+ else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid';
+ else obj.errorIndex = 'none';
+
+ setValuesPassword(obj);
+
+ let objRe = {...valuesPasswordRe};
+ if (text !== valuesPasswordRe.input) objRe.errorIndex = 'noMatch';
+ else objRe.errorIndex = 'none';
+
+ setValuesPasswordRe(objRe);
+ }, 50);
+ },
+ } as inputElementType;
+ const [valuesPassword, setValuesPassword] = useState(password);
+
+ const passwordReRef = useRef(setTimeout(() => {}));
+
+ const passwordRe = {
+ label: lang.account.registration.stepFinal.passwordRepeat,
+ input: '',
+ errorIndex: 'none',
+ errorTextObject: lang.account.registration.stepFinal.passwordError,
+ minLength: passwordOptions.minLength,
+ maxLength: passwordOptions.maxLength,
+ isAllowed: passwordOptions.isAllowed,
+ isPassword: 'passwordRepeat',
+ onTextChange: (e: NativeSyntheticEvent) => {
+ let text = e.nativeEvent.text;
+ let self = passwordRe;
+
+ clearTimeout(passwordReRef.current);
+
+ passwordReRef.current = setTimeout(() => {
+ let obj = {...valuesPasswordRe};
+ obj.input = text;
+ console.log(text, valuesPassword.input);
+
+ if (text !== valuesPassword.input) obj.errorIndex = 'noMatch';
+ else obj.errorIndex = 'none';
+
+ setValuesPasswordRe(obj);
+ }, 50);
+ },
+ } as inputElementType;
+ const [valuesPasswordRe, setValuesPasswordRe] = useState(passwordRe);
+
+ const inputElement = (
+ val: inputElementType,
+ set: React.Dispatch>,
+ valConst: inputElementType,
+ autofocus?: boolean,
+ ) => {
+ const isPassword =
+ val.isPassword === true || val.isPassword === 'passwordRepeat';
+
+ return (
+
+ {val.label}
+ setShowPassword(!showPassword)}
+ icon={
+
+ }
+ borderRadius="full"
+ />
+ ) : undefined
+ }
+ />
+
+ }>
+ {val.errorTextObject[val.errorIndex] !== undefined
+ ? val.errorTextObject[val.errorIndex]
+ .replaceAll('$(minLength)', val.minLength)
+ .replaceAll('$(maxLength)', val.maxLength)
+ : val.errorTextObject[val.errorIndex] !== 'none'
+ ? lang.error
+ : null}
+
+
+ );
+ };
+
+ return (
+
+
+
+
+ {lang.account.registration.stepFinal.displayName}
+
+
+
+ {inputElement(valuesUserName, setValuesUserName, userName)}
+ {inputElement(valuesAccountName, setValuesAccountName, accountName)}
+ {inputElement(valuesPassword, setValuesPassword, password)}
+ {inputElement(valuesPasswordRe, setValuesPasswordRe, passwordRe)}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/caj/components/Toast.tsx b/src/caj/components/Toast.tsx
new file mode 100644
index 0000000..87e893f
--- /dev/null
+++ b/src/caj/components/Toast.tsx
@@ -0,0 +1,142 @@
+import {
+ Alert,
+ Button,
+ Center,
+ CloseIcon,
+ HStack,
+ IconButton,
+ Text,
+ VStack,
+} from 'native-base';
+import {ResponsiveValue} from 'native-base/lib/typescript/components/types';
+
+const {useToast} = require('native-base');
+
+const Toast = () => {
+ const toast = useToast();
+ const ToastDetails = [
+ {
+ title: 'Account verified',
+ variant: 'solid',
+ description: 'Thanks for signing up with us.',
+ isClosable: true,
+ },
+ {
+ title: 'Something went wrong',
+ variant: 'subtle',
+ description: 'Please create a support ticket from the support page',
+ },
+ {
+ title: 'Network connection restored',
+ variant: 'left-accent',
+ description:
+ 'This is to inform you that your network connectivity is restored',
+ isClosable: true,
+ },
+ {
+ title: 'Invalid email address',
+ variant: 'top-accent',
+ description: 'Please enter a valid email address',
+ },
+ {
+ title: 'Invalid email address',
+ variant: 'outline',
+ description: 'Please enter a valid email address',
+ },
+ ];
+};
+
+interface toastType {
+ id?: string;
+ status?: 'info' | (string & {}) | 'error' | 'success' | 'warning';
+ variant: ResponsiveValue<
+ | 'subtle'
+ | 'solid'
+ | 'left-accent'
+ | 'top-accent'
+ | 'outline'
+ | 'outline-light'
+ | (string & {})
+ >;
+ title: any;
+ description: any;
+ isClosable?: boolean;
+ rest: any;
+}
+
+interface alertType extends toastType {
+ id: string;
+}
+
+function showToast(toast: any, item: toastType) {
+ const ToastAlert = ({
+ id,
+ status,
+ variant,
+ title,
+ description,
+ isClosable,
+ rest,
+ }: alertType) => (
+
+
+
+
+
+
+ {title}
+
+
+ {isClosable ? (
+ }
+ _icon={{
+ color: variant === 'solid' ? 'lightText' : 'darkText',
+ }}
+ onPress={() => toast.close(id)}
+ />
+ ) : null}
+
+
+ {description}
+
+
+
+ );
+
+ toast.show({
+ render: ({id}: {id: string}) => {
+ return ;
+ },
+ });
+}
+
+export default showToast;
diff --git a/src/caj/configs/appVar.ts b/src/caj/configs/appVar.ts
index 5572010..3e6388d 100644
--- a/src/caj/configs/appVar.ts
+++ b/src/caj/configs/appVar.ts
@@ -1,3 +1,4 @@
+import {EMail, XToken} from '@caj/helper/types';
import {VersionType} from '@caj/helper/version';
import {APP_VERSION} from './appNonSaveVar';
@@ -43,13 +44,24 @@ export function applyUpdateChanges(appVar: any): Promise {
}
//these variables may be changed by the user and will be saved in storage
+export interface RegisterProcess {
+ isRegistering: false | 'stepTwo' | 'stepFinal';
+ XToken: XToken | undefined;
+ EMail: EMail;
+}
export interface PREFERENCES_VARS {
version: VersionType;
theme: ThemeMode;
+ RegisterProcess: RegisterProcess;
}
export const preferences_vars_default: PREFERENCES_VARS = {
version: APP_VERSION, //version of datatypes in storage
theme: ThemeMode.Dark,
+ RegisterProcess: {
+ isRegistering: false,
+ XToken: undefined,
+ EMail: '',
+ },
};
diff --git a/src/caj/configs/appVarReducer.ts b/src/caj/configs/appVarReducer.ts
index c2dc1a5..527bc70 100644
--- a/src/caj/configs/appVarReducer.ts
+++ b/src/caj/configs/appVarReducer.ts
@@ -1,7 +1,12 @@
import {createSlice} from '@reduxjs/toolkit';
import type {PayloadAction} from '@reduxjs/toolkit';
-import {PREFERENCES_VARS, preferences_vars_default, ThemeMode} from './appVar';
+import {
+ PREFERENCES_VARS,
+ preferences_vars_default,
+ RegisterProcess,
+ ThemeMode,
+} from './appVar';
import {non_save_vars, NON_SAVE_VARS} from './appNonSaveVar';
import LangFormat from '@caj/lang/default';
import {lang as defaultLang} from '@caj/lang/en';
@@ -29,6 +34,9 @@ export const appVariablesSlice = createSlice({
loadPreferences: (state, action: PayloadAction) => {
state.preferences = action.payload;
},
+ setRegisterProcess: (state, action: PayloadAction) => {
+ state.preferences.RegisterProcess = action.payload;
+ },
},
});
diff --git a/src/caj/configs/colors.ts b/src/caj/configs/colors.ts
index c9e17dc..42201dd 100644
--- a/src/caj/configs/colors.ts
+++ b/src/caj/configs/colors.ts
@@ -1,83 +1,194 @@
import {Platform} from 'react-native';
-import {extendTheme, useColorMode} from 'native-base';
+import {extendTheme, Theme, useColorMode} from 'native-base';
import {ThemeMode} from './appVar';
import {useSelector} from 'react-redux';
import {RootState} from '@caj/redux/store';
import {useEffect} from 'react';
-export const theme = extendTheme({
- config: {
- // Changing initialColorMode to 'dark'
- initialColorMode: 'dark',
- },
- fonts: {
- header: 'Outfit-Regular',
- medium: 'Outfit-Regular',
- regular: 'Outfit-Regular',
- semibold: 'Outfit-Regular',
- },
- colors: {
- black: {
- 100: '#C4C4C4',
- 200: '#7C7C7C',
- 300: '#292929',
- 800: '#181725',
+export const theme = (_theme: ThemeMode) => {
+ return extendTheme({
+ config: {
+ // Changing initialColorMode to 'dark'
+ initialColorMode: 'dark',
},
+ fonts: {
+ header: 'Outfit-Regular',
+ medium: 'Outfit-Regular',
+ regular: 'Outfit-Regular',
+ semibold: 'Outfit-Regular',
+ },
+ colors: {
+ black: [
+ {
+ // darkest
+ 100: '#414755',
+ 200: '#313640',
+ 300: '#293037',
+ 400: '#282e35',
+ 500: '#23272e',
+ 600: '#1a1d22',
+ 700: '#19191e',
+ 800: '#101013',
+ 900: '#060607',
+ },
+ {
+ //dark
+ 100: '#414755',
+ 200: '#313640',
+ 300: '#293037',
+ 400: '#282e35',
+ 500: '#23272e',
+ 600: '#1a1d22',
+ 700: '#19191e',
+ 800: '#101013',
+ 900: '#060607',
+ },
+ {
+ //light
+ 900: '#e9e8f0',
+ 800: '#e4e3ec',
+ 700: '#dfdde8',
+ 600: '#dad8e4',
+ 500: '#d7d5e1',
+ 400: '#d4d2de',
+ 300: '#d2d0db',
+ 200: '#cfcdd8',
+ 100: '#cdcbd5',
+ },
+ ][_theme],
+ blackBG: [
+ {
+ // darkest
+ 100: '#000',
+ 200: '#000',
+ 300: '#000',
+ 400: '#000',
+ 500: '#000',
+ 600: '#000',
+ 700: '#000',
+ 800: '#000',
+ 900: '#000',
+ },
+ {
+ //dark
+ 100: '#414755',
+ 200: '#313640',
+ 300: '#293037',
+ 400: '#282e35',
+ 500: '#23272e',
+ 600: '#1a1d22',
+ 700: '#19191e',
+ 800: '#101013',
+ 900: '#060607',
+ },
+ {
+ //light
+ 900: '#e9e8f0',
+ 800: '#e4e3ec',
+ 700: '#dfdde8',
+ 600: '#dad8e4',
+ 500: '#d7d5e1',
+ 400: '#d4d2de',
+ 300: '#d2d0db',
+ 200: '#cfcdd8',
+ 100: '#cdcbd5',
+ },
+ ][_theme],
+ white: [
+ {
+ // darkest
+ 100: '#ccc',
+ 200: '#ccc',
+ 300: '#ccc',
+ 400: '#ccc',
+ 500: '#ccc',
+ 600: '#ccc',
+ 700: '#ccc',
+ 800: '#ccc',
+ 900: '#ccc',
+ },
+ {
+ //dark
- primary: {
- 50: '#fff4f1',
- 100: '#ffd6c9',
- 200: '#ffb9a1',
- 300: '#ff9b79',
- 400: '#ff7d50',
- 500: '#f96e40',
- 600: '#f26030',
- 700: '#e95321',
- 800: '#d54b1d',
- 900: '#ba4721',
- },
- },
- components: {
- Divider: {
- baseStyle: ({colorMode}) => {
- return {
- backgroundColor: colorMode === 'dark' ? 'black.100' : 'black.200',
- };
+ 900: '#e9e8f0',
+ 800: '#e4e3ec',
+ 700: '#dfdde8',
+ 600: '#dad8e4',
+ 500: '#d7d5e1',
+ 400: '#d4d2de',
+ 300: '#d2d0db',
+ 200: '#cfcdd8',
+ 100: '#cdcbd5',
+ },
+ {
+ //light
+ 100: '#414755',
+ 200: '#313640',
+ 300: '#293037',
+ 400: '#282e35',
+ 500: '#23272e',
+ 600: '#1a1d22',
+ 700: '#19191e',
+ 800: '#101013',
+ 900: '#060607',
+ },
+ ][_theme],
+
+ primary: {
+ 50: '#fff4f1',
+ 100: '#ffd6c9',
+ 200: '#ffb9a1',
+ 300: '#ff9b79',
+ 400: '#ff7d4f',
+ 500: '#f96e40',
+ 600: '#f26030',
+ 700: '#e95321',
+ 800: '#d54b1d',
+ 900: '#ba4721',
},
},
- Text: {
- defaultProps: {
- size: 'md',
- fontFamily: 'Outfit-Regular',
- colorScheme: 'red',
- },
- sizes: {
- xl: {
- fontSize: '64px',
- },
- lg: {
- fontSize: '32px',
- },
- md: {
- fontSize: '16px',
- },
- sm: {
- fontSize: '12px',
+ components: {
+ Divider: {
+ baseStyle: ({colorMode}) => {
+ return {
+ backgroundColor: colorMode === 'dark' ? 'black.100' : 'black.200',
+ };
},
},
- Button: {
- // Can simply pass default props to change default behaviour of components.
- baseStyle: {
- rounded: 'md',
- },
+ Text: {
defaultProps: {
- colorScheme: 'red',
+ size: 'md',
+ fontFamily: 'Outfit-Regular',
+ colorScheme: 'black',
+ },
+ sizes: {
+ xl: {
+ fontSize: '64px',
+ },
+ lg: {
+ fontSize: '32px',
+ },
+ md: {
+ fontSize: '16px',
+ },
+ sm: {
+ fontSize: '12px',
+ },
+ },
+ Button: {
+ // Can simply pass default props to change default behaviour of components.
+ baseStyle: {
+ rounded: 'md',
+ },
+ defaultProps: {
+ colorScheme: 'primary',
+ },
},
},
},
- },
-});
+ });
+};
export function getBackgroundColor(tm: ThemeMode): string {
switch (tm) {
@@ -86,7 +197,7 @@ export function getBackgroundColor(tm: ThemeMode): string {
case ThemeMode.Darkest:
return '#000';
default:
- return '#282f34';
+ return '#1b1c22';
}
}
@@ -117,3 +228,26 @@ export function ThemeSwitcher() {
return null;
}
+
+export const defaultHeaderStyle = (cur: ThemeMode, place?: 'registration') => {
+ if (place === 'registration') {
+ return {
+ headerTintColor: '#fff',
+ headerStyle: {
+ backgroundColor: theme(cur).colors.primary[500],
+ },
+ };
+ }
+
+ return {
+ headerTintColor: theme(cur).colors.primary[400],
+ headerStyle: {
+ backgroundColor:
+ cur === ThemeMode.Light
+ ? '#eee'
+ : cur === ThemeMode.Dark
+ ? '#181d21'
+ : '#000',
+ },
+ };
+};
diff --git a/src/caj/helper/animations.tsx b/src/caj/helper/animations.tsx
new file mode 100644
index 0000000..395ac34
--- /dev/null
+++ b/src/caj/helper/animations.tsx
@@ -0,0 +1,132 @@
+import {useFocusEffect} from '@react-navigation/native';
+import {animated, useSpring} from '@react-spring/native';
+import {useEffect} from 'react';
+import {Dimensions, View} from 'react-native';
+
+const AnimationView = animated(View);
+
+export const FadeInView = (props: any) => {
+ const [motionProps, api] = useSpring(
+ () => ({
+ from: {
+ opacity: 0,
+ },
+ }),
+ [],
+ );
+
+ useFocusEffect(() => {
+ api.start({
+ to: [
+ {
+ opacity: 1,
+ },
+ ],
+ });
+ return () => {
+ api.start({
+ to: [
+ {
+ opacity: 0,
+ },
+ ],
+ });
+ };
+ });
+
+ return (
+
+ {props.children}
+
+ );
+};
+
+export const SlideFromLeftView = (props: any) => {
+ let SCREEN_WIDTH = Dimensions.get('window').width;
+
+ const [motionProps, api] = useSpring(
+ () => ({
+ from: {
+ translateX: -SCREEN_WIDTH,
+ },
+ }),
+ [],
+ );
+
+ useFocusEffect(() => {
+ api.start({
+ to: [
+ {
+ translateX: 0,
+ },
+ ],
+ });
+ return () => {
+ SCREEN_WIDTH = Dimensions.get('window').width;
+ api.start({
+ to: [
+ {
+ translateX: -SCREEN_WIDTH,
+ },
+ ],
+ });
+ };
+ });
+
+ return (
+
+ {props.children}
+
+ );
+};
+
+export const SlideFromRightView = (props: any) => {
+ let SCREEN_WIDTH = Dimensions.get('window').width;
+
+ const [motionProps, api] = useSpring(
+ () => ({
+ from: {
+ translateX: SCREEN_WIDTH,
+ },
+ }),
+ [],
+ );
+
+ useFocusEffect(() => {
+ api.start({
+ to: [
+ {
+ translateX: 0,
+ },
+ ],
+ });
+ return () => {
+ SCREEN_WIDTH = Dimensions.get('window').width;
+ api.start({
+ to: [
+ {
+ translateX: SCREEN_WIDTH,
+ },
+ ],
+ });
+ };
+ });
+
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/src/caj/helper/request.ts b/src/caj/helper/request.ts
new file mode 100644
index 0000000..2b9f50f
--- /dev/null
+++ b/src/caj/helper/request.ts
@@ -0,0 +1,168 @@
+import {types} from '@babel/core';
+import {store} from '@caj/redux/store';
+import {Any} from '@react-spring/types';
+import {type} from 'os';
+import {Platform} from 'react-native';
+
+import getUserAgent from './userAgent';
+
+import {
+ AccountName,
+ EMail,
+ Password,
+ SessionId,
+ UserId,
+ Username,
+ verifyId,
+ WebSocketSessionId,
+ XToken,
+} from './types';
+
+export const apiPath = {
+ backend: {
+ prefix: 'https://alpha-api.clickandjoin.umbach.dev/v1',
+ },
+};
+
+export enum apiBackendRequest {
+ REGISTER_STEP_1 = '/admin/users/email',
+ REGISTER_RESEND_MAIL = '/admin/users/email/resend',
+ REGISTER_STEP_2 = '/verify/email/:xToken/:verifyId',
+ REGISTER_STEP_FINAL = '/admin/users',
+ REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK = '/admin/users/validation/accountname',
+}
+
+type requestGET = {[key: string]: string};
+
+export interface defaultRequest {
+ status?: 200 | 400 | 422 | 500;
+ error?: string;
+ requestGET?: requestGET;
+}
+
+interface REGISTER_STEP_1 extends defaultRequest {
+ path: apiBackendRequest.REGISTER_STEP_1;
+ requestHeader: {};
+ request: {
+ Email: EMail;
+ };
+ response: {
+ XToken: XToken | undefined;
+ };
+}
+interface REGISTER_RESEND_MAIL extends defaultRequest {
+ path: apiBackendRequest.REGISTER_RESEND_MAIL;
+ requestHeader: {};
+ request: {
+ Email: EMail;
+ };
+ // response: {
+ // XToken: XToken | undefined;
+ //};
+}
+
+interface REGISTER_STEP_2 extends defaultRequest {
+ path: apiBackendRequest.REGISTER_STEP_2;
+ requestHeader: {};
+ requestGET: {
+ ':xToken': XToken;
+ ':verifyId': verifyId;
+ };
+}
+
+interface REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK extends defaultRequest {
+ path: apiBackendRequest.REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK;
+
+ request: {
+ AccountName: AccountName;
+ };
+ // response: {
+ // XToken: XToken | undefined;
+ //};
+}
+
+interface REGISTER_STEP_FINAL extends defaultRequest {
+ path: apiBackendRequest.REGISTER_STEP_FINAL;
+ requestHeader: {
+ XToken: XToken;
+ };
+ request: {
+ AccountName: AccountName;
+ Username: Username;
+ Password: Password;
+ };
+ response?: {
+ SessionId: SessionId;
+ UserId: UserId;
+ WebSocketSessionId: WebSocketSessionId;
+ };
+}
+
+type FetchTypes =
+ | REGISTER_STEP_1
+ | REGISTER_RESEND_MAIL
+ | REGISTER_STEP_2
+ | REGISTER_STEP_FINAL
+ | REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK;
+
+function isA(obj: any): obj is REGISTER_STEP_1 {
+ return obj.request !== undefined;
+}
+
+export function makeRequest(type: T1): Promise {
+ let makeRequestObj: any = type;
+
+ let path = type.path.toString();
+
+ if (type.requestGET !== undefined) {
+ for (let key in type.requestGET) {
+ path = path.replaceAll(key, (type.requestGET as requestGET)[key]);
+ }
+ }
+
+ // Simple POST request with a JSON body using fetch
+
+ let headers: HeadersInit = {
+ 'Content-Type': 'application/json',
+ 'X-App-Version': store
+ .getState()
+ .nonSaveVariables.currentAppVersion.toString(),
+ 'X-Language': store.getState().appVariables.lang.details.langCode,
+ };
+
+ if (Platform.OS === 'android' || Platform.OS === 'ios')
+ headers['User-Agent'] = getUserAgent();
+
+ const requestOptions: RequestInit = {
+ method: makeRequestObj.request !== undefined ? 'POST' : 'GET',
+
+ headers,
+ body:
+ makeRequestObj.request !== undefined
+ ? JSON.stringify(makeRequestObj.request)
+ : undefined,
+ };
+
+ return new Promise((resolve, reject) => {
+ fetch(apiPath.backend.prefix + path, requestOptions)
+ .then(response => {
+ makeRequestObj.status = response.status;
+
+ if (makeRequestObj.response === undefined) return {};
+
+ return response.json();
+ })
+ .then(data => {
+ makeRequestObj.response = data;
+
+ //console.log(makeRequestObj);
+ if (makeRequestObj.status !== 200) reject(makeRequestObj);
+ else resolve(makeRequestObj);
+ })
+ .catch(error => {
+ //console.error(error, makeRequestObj);
+ makeRequestObj.error = error;
+ reject(makeRequestObj);
+ });
+ });
+}
diff --git a/src/caj/helper/types.ts b/src/caj/helper/types.ts
new file mode 100644
index 0000000..59fc160
--- /dev/null
+++ b/src/caj/helper/types.ts
@@ -0,0 +1,44 @@
+export type EMail = string;
+
+export type AccountName = string;
+export type Username = string;
+export type Password = string;
+export type UserAgent = string;
+export type XToken = string;
+export type verifyId = string;
+
+export type SessionId = string;
+export type UserId = string;
+export type WebSocketSessionId = string;
+
+export const accountNameOptions = {
+ minLength: 4,
+ maxLength: 24,
+ isAllowed: (text: string): boolean => {
+ return text.match('^[a-zA-Z0-9_.]+$') !== null;
+ },
+};
+export const userNameOptions = {
+ minLength: 2,
+ maxLength: 24,
+ isAllowed: (text: string): boolean => {
+ return true;
+ },
+};
+
+export const passwordOptions = {
+ minLength: 6,
+ maxLength: 64,
+ isAllowed: (text: string): boolean => {
+ return true;
+ },
+};
+
+export const emailOptions = {
+ maxLength: 48,
+ isAllowed: (email: string) => {
+ return String(email).match(
+ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
+ );
+ },
+};
diff --git a/src/caj/helper/userAgent.ts b/src/caj/helper/userAgent.ts
new file mode 100644
index 0000000..5a0c8e3
--- /dev/null
+++ b/src/caj/helper/userAgent.ts
@@ -0,0 +1,3 @@
+import {getUserAgent} from 'react-native-user-agent';
+
+export default getUserAgent;
diff --git a/src/caj/helper/userAgent.web.ts b/src/caj/helper/userAgent.web.ts
new file mode 100644
index 0000000..84e5410
--- /dev/null
+++ b/src/caj/helper/userAgent.web.ts
@@ -0,0 +1,2 @@
+const getUserAgent = undefined;
+export default getUserAgent;
diff --git a/src/caj/lang/default.ts b/src/caj/lang/default.ts
index 7b8c58f..d27acfb 100644
--- a/src/caj/lang/default.ts
+++ b/src/caj/lang/default.ts
@@ -9,7 +9,56 @@ export default interface LangFormat {
details: LangDetails;
curVersion: string;
appName: string;
+ appNameDesc: string;
startHelper1: string;
+ navigation: {
+ home: {
+ account: string;
+ calendar: string;
+ chat: string;
+ };
+ };
+ info: string;
+ error: string;
+ success: string;
+ account: {
+ registration: {
+ registration: string;
+ info: string;
+ privacyPolicy: string;
+ termsOfUse: string;
+ stepOne: {
+ title: string;
+ success: string;
+ addressExists: string;
+ addressInvalid: string;
+ noMailEntered: string;
+ button: string;
+ };
+ stepTwo: {
+ title: string;
+ verification: string;
+ resend: [string, string, string];
+ noCodeEntered: string;
+ resendError: {[key: number]: string};
+ verificationError: {[key: number]: string};
+ button: string;
+ success: string;
+ };
+ stepFinal: {
+ verification: string;
+ userName: string;
+ accountName: string;
+ password: string;
+ passwordRepeat: string;
+ displayName: string;
+ accountNameError: {[key: string]: string};
+ userNameError: {[key: string]: string};
+ passwordError: {[key: string]: string};
+ button: string;
+ };
+ };
+ };
}
interface LangPlaceholderKeys {
diff --git a/src/caj/lang/en.ts b/src/caj/lang/en.ts
index 4076eaf..d8d60c5 100644
--- a/src/caj/lang/en.ts
+++ b/src/caj/lang/en.ts
@@ -8,5 +8,85 @@ export const lang: LangFormat = {
},
curVersion: 'Your current version is v${version}.',
appName: 'Click And Join',
+ appNameDesc: 'an app developed with love by Janex',
startHelper1: 'Your data will be loaded :)',
+ navigation: {
+ home: {
+ account: 'My account',
+ calendar: 'Calendar',
+ chat: 'Conversations',
+ },
+ },
+ info: 'Info',
+ error: 'Error',
+ success: 'Success',
+ account: {
+ registration: {
+ registration: 'Registration',
+ info: 'By registering, you agree to our ${TermsOfUse}. You can find out how we collect and use your data in our ${privacyPolicy}.',
+ privacyPolicy: 'privacy policy',
+ termsOfUse: 'Terms of Use',
+ stepOne: {
+ title: 'Enter E-Mail',
+ success: 'A verification has sent to your E-Mail!',
+ addressExists: 'The E-Mail you entered is already in use.',
+ addressInvalid: 'The address you entered has an invalid format.',
+ noMailEntered: 'Please enter your E-Mail',
+ button: 'Next step',
+ },
+ stepTwo: {
+ title: 'Enter the 6-digit code that we sent you to ${EMail}.',
+ verification: 'X-X-X-X-X-X',
+ noCodeEntered: 'Please enter your code',
+ resend: [
+ 'E-Mail not arrived? You can also resend the verification E-Mail by ${resend}.',
+ 'clicking here',
+ 'E-Mail verification has resent',
+ ],
+ resendError: {
+ 400: 'The E-Mail is already verified!',
+ 401: 'Your device have changed. Please use another E-Mail address.',
+ 429: 'Too many requests in a too small period of time in a row',
+ },
+ verificationError: {
+ 400: 'Something went wrong please try again :( Restart App maybe required',
+ 401: 'The code you entered does not match with the code we sent you',
+ 422: 'The verification time is expired. Please please try again',
+ },
+ button: 'Verify',
+ success: 'Verification success!',
+ },
+ stepFinal: {
+ verification:
+ 'Your Account has been verified. Now enter your user credentials.',
+ userName: 'Username',
+ accountName: 'AccountName',
+ password: 'Password',
+ passwordRepeat: 'Repeat password',
+ displayName: 'Other users see you like this:',
+ accountNameError: {
+ tooLong: 'Too long. Max length are $(maxLength) character.',
+ tooShort: 'Too short. Min length are $(minLength) character.',
+ required: 'This field is required',
+ invalid:
+ 'Account names can only contain letters, numbers, underscores (_) and dots (.)',
+ exists: 'The account name you entered already exists.',
+ },
+ userNameError: {
+ tooLong: 'Too long. Max length are $(maxLength) character.',
+ tooShort: 'Too short. Min length are $(minLength) character.',
+ required: 'This field is required',
+ },
+ passwordError: {
+ noMatch: 'Passwords do not match',
+ tooLong: 'Too long. Max length are $(maxLength) character.',
+ tooShort: 'Too short. Min length are $(minLength) character.',
+ required: 'This field is required',
+ weak: 'Password is too weak',
+ },
+
+ button: 'Finish registration',
+ },
+ },
+ },
};
diff --git a/src/caj/tabs/main/AccountTab.tsx b/src/caj/tabs/main/AccountTab.tsx
new file mode 100644
index 0000000..df1a73c
--- /dev/null
+++ b/src/caj/tabs/main/AccountTab.tsx
@@ -0,0 +1,71 @@
+import AccountInfoBanner from '@caj/components/AccountInfoBanner';
+import NotLoggedIn from '@caj/components/NotLoggedIn';
+import {ThemeMode} from '@caj/configs/appVar';
+import {defaultHeaderStyle} from '@caj/configs/colors';
+import {RootStackNavigatorParamList} from '@caj/Navigation';
+import {RootState} from '@caj/redux/store';
+import {
+ createNativeStackNavigator,
+ NativeStackNavigationProp,
+} from '@react-navigation/native-stack';
+import {Box, Center, Container, Text} from 'native-base';
+import {useSelector} from 'react-redux';
+
+export const AccountTabName = 'Account';
+
+export type AccountStackNavigatorParamList = {
+ Overview: undefined;
+};
+
+const AccountStack =
+ createNativeStackNavigator();
+
+export type AccountScreenNavigationProp =
+ NativeStackNavigationProp;
+
+function AccountTab() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+
+ return (
+
+
+
+ );
+}
+
+function AccountScreen() {
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+ return (
+ <>
+
+
+
+
+ >
+ );
+}
+
+export default AccountTab;
diff --git a/src/caj/tabs/main/CalendarTab.tsx b/src/caj/tabs/main/CalendarTab.tsx
new file mode 100644
index 0000000..0d0d2a8
--- /dev/null
+++ b/src/caj/tabs/main/CalendarTab.tsx
@@ -0,0 +1,53 @@
+import NotLoggedIn from '@caj/components/NotLoggedIn';
+import {defaultHeaderStyle} from '@caj/configs/colors';
+import {RootStackNavigatorParamList} from '@caj/Navigation';
+import {RootState} from '@caj/redux/store';
+import {
+ createNativeStackNavigator,
+ NativeStackNavigationProp,
+} from '@react-navigation/native-stack';
+import {Center, Text} from 'native-base';
+import {useSelector} from 'react-redux';
+
+export const CalendarTabName = 'Calendar';
+
+export type CalendarStackNavigatorParamList = {
+ Overview: undefined;
+};
+
+const CalendarStack =
+ createNativeStackNavigator();
+
+export type CalendarScreenNavigationProp =
+ NativeStackNavigationProp;
+
+function CalendarTab() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+
+ return (
+
+
+
+ );
+}
+
+function CalendarScreen() {
+ return (
+
+
+
+ );
+}
+
+export default CalendarTab;
diff --git a/src/caj/tabs/main/ChatTab.tsx b/src/caj/tabs/main/ChatTab.tsx
new file mode 100644
index 0000000..1534926
--- /dev/null
+++ b/src/caj/tabs/main/ChatTab.tsx
@@ -0,0 +1,52 @@
+import NotLoggedIn from '@caj/components/NotLoggedIn';
+import {defaultHeaderStyle} from '@caj/configs/colors';
+import {RootStackNavigatorParamList} from '@caj/Navigation';
+import {RootState} from '@caj/redux/store';
+import {
+ createNativeStackNavigator,
+ NativeStackNavigationProp,
+} from '@react-navigation/native-stack';
+import {Center, Text} from 'native-base';
+import {useSelector} from 'react-redux';
+
+export const ChatTabName = 'Chat';
+
+export type ChatStackNavigatorParamList = {
+ Overview: undefined;
+};
+
+const ChatStack = createNativeStackNavigator();
+
+export type ChatScreenNavigationProp =
+ NativeStackNavigationProp;
+
+function ChatTab() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+
+ return (
+
+
+
+ );
+}
+
+function ChatScreen() {
+ return (
+
+
+
+ );
+}
+
+export default ChatTab;
diff --git a/src/caj/tabs/main/MapsTab.tsx b/src/caj/tabs/main/MapsTab.tsx
new file mode 100644
index 0000000..3c0768e
--- /dev/null
+++ b/src/caj/tabs/main/MapsTab.tsx
@@ -0,0 +1,47 @@
+import {defaultHeaderStyle} from '@caj/configs/colors';
+import {RootStackNavigatorParamList} from '@caj/Navigation';
+import {RootState} from '@caj/redux/store';
+import {
+ createNativeStackNavigator,
+ NativeStackNavigationProp,
+} from '@react-navigation/native-stack';
+import {Center, Text} from 'native-base';
+import {useSelector} from 'react-redux';
+
+export const MapsTabName = 'Maps';
+
+export type MapsStackNavigatorParamList = {
+ Overview: undefined;
+};
+
+const MapsStack = createNativeStackNavigator();
+
+export type MapsScreenNavigationProp =
+ NativeStackNavigationProp;
+
+function MapsTab() {
+ const lang = useSelector((state: RootState) => state.appVariables.lang);
+ const theme = useSelector(
+ (state: RootState) => state.appVariables.preferences.theme,
+ );
+
+ return (
+
+
+
+ );
+}
+
+function MapsScreen() {
+ return MapsScreen;
+}
+
+export default MapsTab;
diff --git a/web/public/index.html b/web/public/index.html
index 2dce563..c760e59 100644
--- a/web/public/index.html
+++ b/web/public/index.html
@@ -12,52 +12,6 @@
--doc--height: 100%;
}
- @font-face {
- font-family: 'Outfit-Thin';
- src: url('/fonts/Outfit.ttf');
- font-weight: 100;
- }
- @font-face {
- font-family: 'Outfit-ExtraLight';
- src: url('/fonts/Outfit.ttf');
- font-weight: 200;
- }
- @font-face {
- font-family: 'Outfit-Light';
- src: url('/fonts/Outfit.ttf');
- font-weight: 300;
- }
- @font-face {
- font-family: 'Outfit-Regular';
- src: url('/fonts/Outfit.ttf');
- font-weight: 400;
- }
- @font-face {
- font-family: 'Outfit-Medium';
- src: url('/fonts/Outfit.ttf');
- font-weight: 500;
- }
- @font-face {
- font-family: 'Outfit-SemiBold';
- src: url('/fonts/Outfit.ttf');
- font-weight: 600;
- }
- @font-face {
- font-family: 'Outfit-Bold';
- src: url('/fonts/Outfit.ttf');
- font-weight: 700;
- }
- @font-face {
- font-family: 'Outfit-ExtraBold';
- src: url('/fonts/Outfit.ttf');
- font-weight: 800;
- }
- @font-face {
- font-family: 'Outfit-Black';
- src: url('/fonts/Outfit.ttf');
- font-weight: 900;
- }
-
html,
body {
padding: 0;
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 32ba0d9..d9f0837 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -11,13 +11,14 @@ const appDirectory = path.resolve(__dirname, '../');
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
- test: /\.(ts|tsx|js)?$/,
+ test: /\.(ts|tsx|js|jsx)?$/,
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(appDirectory, 'index.web.tsx'),
path.resolve(appDirectory, 'src'),
+ /node_modules\/react-native-/,
],
- exclude: [path.resolve(appDirectory, 'node_modules')],
+ // exclude: [path.resolve(appDirectory, 'node_modules')],
use: {
loader: 'babel-loader',
options: {
@@ -25,14 +26,20 @@ const babelLoaderConfiguration = {
// The 'react-native' preset is recommended to match React Native's packager
// presets: ['module:metro-react-native-babel-preset'],
// presets: ['react-native'],
- presets: [require.resolve('babel-preset-react-native')],
- // Re-write paths to import only the modules needed by the app
- plugins: ['react-native-web'],
- presets: ['react-native'],
+
presets: ['module:metro-react-native-babel-preset'],
plugins: [
// needed to support async/await
- '@babel/plugin-transform-runtime',
+ // '@babel/plugin-transform-runtime',
+ 'react-native-web',
+ [
+ 'module-resolver',
+ {
+ alias: {
+ '^react-native$': 'react-native-web',
+ },
+ },
+ ],
],
},
},
@@ -49,9 +56,26 @@ const imageLoaderConfiguration = {
},
};
+const ttfLoaderConfiguration = {
+ test: /\.ttf$/,
+ loader: 'url-loader', // or directly file-loader
+ include: [
+ path.resolve(appDirectory, 'node_modules/react-native-vector-icons'),
+ path.resolve(appDirectory, 'web/public/fonts'),
+ ],
+};
+
module.exports = {
mode: 'development',
target: 'web',
+ devServer: {
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
+ 'Access-Control-Allow-Headers':
+ 'X-Requested-With, content-type, Authorization',
+ },
+ },
entry: [
// load any web API polyfills
// path.resolve(appDirectory, 'polyfills-web.js'),
@@ -68,7 +92,11 @@ module.exports = {
// ...the rest of your config
module: {
- rules: [babelLoaderConfiguration, imageLoaderConfiguration],
+ rules: [
+ babelLoaderConfiguration,
+ imageLoaderConfiguration,
+ ttfLoaderConfiguration,
+ ],
},
resolve: {
@@ -80,6 +108,15 @@ module.exports = {
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
- extensions: ['.web.js', '.js', '.web.ts', '.ts', '.web.tsx', '.tsx'],
+ extensions: [
+ '.web.js',
+ '.web.jsx',
+ '.js',
+ '.jsx',
+ '.web.ts',
+ '.ts',
+ '.web.tsx',
+ '.tsx',
+ ],
},
};