alpha
Jan Umbach 2023-01-04 18:29:11 +01:00
parent 10ac84df72
commit 8b7f244812
29 changed files with 2751 additions and 184 deletions

View File

@ -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"

View File

@ -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(<App />);

300
package-lock.json generated
View File

@ -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",

View File

@ -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)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "ts-jest"

View File

@ -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 (
<NavigationContainer theme={navigationTheme} linking={linking}>
<NativeBaseProvider theme={theme}>
<NavigationContainer theme={navigationTheme} /*linking={linking}*/>
<NativeBaseProvider theme={theme(globalTheme)}>
<MainComponent />
</NativeBaseProvider>
</NavigationContainer>

View File

@ -23,7 +23,7 @@ function onAppStart() {
console.log('finish');
setTimeout(() => {
store.dispatch(appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING));
}, 500);
}, 250);
//store.dispatch(actions.loadPreferences(appVar));
});
}

View File

@ -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<SettingsStackNavigatorParamList>;
function SettingsScreen() {
const navigation = useNavigation<HomeScreenNavigationProp>();
const navigation = useNavigation<SettingsScreenNavigationProp>();
const navigation2 = useNavigation<HomeScreenNavigationProp>();
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Settings screen</Text>
<Button onPress={() => navigation.navigate('Chat')}>Go to Chat</Button>
<Button onPress={() => navigation2.navigate('Maps')}>Go to Chat</Button>
</View>
);
}

126
src/caj/Navigation.tsx Normal file
View File

@ -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<HomeStackNavigatorParamList>;
Register: NavigatorScreenParams<LoginStackNavigatorParamList>;
};
export type RootScreenNavigationProp =
NativeStackNavigationProp<RootStackNavigatorParamList>;
export default function Navigation() {
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Home" component={HomeStack} />
<Stack.Screen name="Register" component={RegisterScreenAnim} />
</Stack.Navigator>
);
}
const Stack = createNativeStackNavigator<RootStackNavigatorParamList>();
export type HomeStackNavigatorParamList = {
Account: NavigatorScreenParams<AccountStackNavigatorParamList>;
Calendar: NavigatorScreenParams<CalendarStackNavigatorParamList>;
Maps: NavigatorScreenParams<MapsStackNavigatorParamList>;
Chat: NavigatorScreenParams<ChatStackNavigatorParamList>;
};
function AccountTabAnim(props: any) {
return (
<SlideFromLeftView>
<AccountTab {...props} />
</SlideFromLeftView>
);
}
function CalendarTabAnim(props: any) {
return (
<FadeInView>
<CalendarTab {...props} />
</FadeInView>
);
}
function MapsTabAnim(props: any) {
return (
<FadeInView>
<MapsTab {...props} />
</FadeInView>
);
}
function ChatTabAnim(props: any) {
return (
<SlideFromRightView>
<ChatTab {...props} />
</SlideFromRightView>
);
}
function HomeStack() {
return (
<Tab.Navigator screenOptions={{headerShown: false, unmountOnBlur: false}}>
<Tab.Screen name="Account" component={AccountTabAnim} />
<Tab.Screen name="Calendar" component={CalendarTabAnim} />
<Tab.Screen name="Maps" component={MapsTabAnim} />
<Tab.Screen name="Chat" component={ChatTabAnim} />
</Tab.Navigator>
);
}
const Tab = createBottomTabNavigator<HomeStackNavigatorParamList>();

View File

@ -0,0 +1,5 @@
import {Center} from 'native-base';
export default function AccountInfoBanner() {
return <Center>Account</Center>;
}

View File

@ -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 (
<Pressable
{...props.rest}
w={10}
h={10}
borderColor={props.isFocus ? 'primary.300' : 'black.100'}
backgroundColor={
props.disabled ? 'black.300' : props.isFocus ? '#ff9b7933' : null
}
borderWidth={1}
borderRadius={5}>
<Center w={'100%'} h={'100%'}>
<Text
fontSize={'2xl'}
color={props.disabled ? 'white.100' : 'white.900'}
{...props.rest}>
{props.isFocus ? (toggle ? '|' : '') : display}
</Text>
</Center>
</Pressable>
);
}
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<TextInput>(null);
return (
<Box {...props.rest}>
<TextInput
autoFocus
ref={inputRef}
value={values.input}
keyboardType={props.charType === 'number' ? 'numeric' : 'default'}
onFocus={() => setFocus(true)}
onBlur={() => setFocus(false)}
editable={!props.disabled}
onChange={(event: NativeSyntheticEvent<TextInputChangeEventData>) => {
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,
}}
/>
<HStack space={3} justifyContent="center">
{(() => {
let cells = [];
for (let i = 0; i < props.cellCount; i++) {
cells.push(
<RenderCell
key={'ConfirmationCodeFieldCell' + i}
index={i}
isFocus={isFocus === true ? values.input.length === i : false}
text={values.input[i]}
maxIndex={props.cellCount}
disabled={props.disabled || false}
rest={{
onPress: !props.disabled
? () => {
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;
})()}
</HStack>
</Box>
);
}
export default ConfirmationCodeField;

View File

@ -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 (
<HStack space={fontSize / 30} alignItems={'flex-end'}>
<Text fontSize={fontSize} color="primary.400">
{props.UserName !== '' ? props.UserName : '----'}
</Text>
<Text
fontSize={fontSize / 1.5}
fontFamily={'Outfit-Light'}
color="light.400">
{props.AccountName !== '' ? props.AccountName : '----'}
</Text>
</HStack>
);
}

View File

@ -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<RootScreenNavigationProp>();
return (
<Box alignItems="center" mt={5}>
<Text color="primary.400">{lang.appName}</Text>
<Text color="white.100">{lang.appNameDesc}</Text>
<VStack mt={5} space={4} alignItems="center">
<Button
w="72"
colorScheme="primary"
rounded="xl"
_text={{fontSize: 'xl'}}
onPress={() => {
navigation.navigate('Register', {screen: 'RegStepOne'});
}}>
Sign up
</Button>
<Button
w="72"
colorScheme="black"
variant={theme === ThemeMode.Darkest ? 'outline' : 'subtle'}
rounded="xl"
_text={{fontSize: 'xl', color: 'white.900'}}>
Log in
</Button>
</VStack>
</Box>
);
}
export function RegisterScreenAnim(props: any) {
return (
<SlideFromLeftView>
<RegisterScreen {...props} />
</SlideFromLeftView>
);
}
export type LoginStackNavigatorParamList = {
RegStepOne: undefined;
RegStepTwo: undefined;
RegStepFinal: undefined;
};
const LoginStack = createNativeStackNavigator<LoginStackNavigatorParamList>();
export type LoginScreenNavigationProp =
NativeStackNavigationProp<LoginStackNavigatorParamList>;
function RegisterScreen() {
const lang = useSelector((state: RootState) => state.appVariables.lang);
const theme = useSelector(
(state: RootState) => state.appVariables.preferences.theme,
);
return (
<LoginStack.Navigator>
<LoginStack.Screen
name="RegStepOne"
options={{
animation: 'slide_from_left',
title: lang.account.registration.registration,
headerShown: true,
...defaultHeaderStyle(theme, 'registration'),
}}
component={StepOne}
/>
<LoginStack.Screen
name="RegStepTwo"
options={{
animation: 'slide_from_right',
title: lang.account.registration.registration,
headerShown: true,
...defaultHeaderStyle(theme, 'registration'),
}}
component={StepTwo}
/>
<LoginStack.Screen
name="RegStepFinal"
options={{
animation: 'slide_from_right',
title: lang.account.registration.registration,
headerShown: true,
...defaultHeaderStyle(theme, 'registration'),
}}
component={StepFinal}
/>
</LoginStack.Navigator>
);
}
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) => (
<Text
key={match + i}
color={textColor}
bold
onPress={() => {
showToast(toast, {
title: lang.account.registration.termsOfUse,
variant: 'solid',
status: 'info',
description: undefined,
isClosable: true,
rest: {colorScheme: 'primary'},
});
}}>
{lang.account.registration.termsOfUse}
</Text>
),
);
replacedText = reactStringReplace(
replacedText,
'${privacyPolicy}',
(match, i) => (
<Text
key={match + i}
color={textColor}
bold
onPress={() => {
showToast(toast, {
title: lang.account.registration.privacyPolicy,
variant: 'solid',
status: 'info',
description: undefined,
isClosable: true,
rest: {colorScheme: 'primary'},
});
}}>
{lang.account.registration.privacyPolicy}
</Text>
),
);
return (
<Text textAlign={'justify'} color={'white.900'}>
{replacedText}
</Text>
);
}
function resendMail(email: EMail, toast: any): Promise<XToken> {
return new Promise<XToken>((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<RootScreenNavigationProp>();
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 (
<ScrollView keyboardShouldPersistTaps="handled">
<Box alignItems="center" mt={5}>
<FormControl
w="75%"
maxW="350px"
isRequired
isDisabled={isLoading}
isInvalid={isError}>
<FormControl.Label>
{lang.account.registration.stepOne.title}
</FormControl.Label>
<Input
autoFocus
placeholder={lang.account.registration.stepOne.title}
value={values.email}
autoCapitalize={'none'}
maxLength={emailOptions.maxLength}
keyboardType={'email-address'}
onChangeText={text => {
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});
}
}}
/>
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
{errorText()}
</FormControl.ErrorMessage>
</FormControl>
<Container marginY={5}>
<Agreement />
</Container>
<Button
w="75%"
maxW="350px"
colorScheme="primary"
rounded="xl"
_text={{fontSize: 'xl'}}
isLoading={isLoading}
onPress={() => {
if (values.email === '') {
let err = errors;
err.noEntered = true;
setErrors({...err});
} else if (validateEmail(values.email)) {
nextStep();
} else {
let err = errors;
err.wrongFormat = true;
setErrors({...err});
}
}}>
{lang.account.registration.stepOne.button}
</Button>
</Box>
</ScrollView>
);
}
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<RootScreenNavigationProp>();
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) => (
<Text key={match + i} color={'primary.400'}>
{regPro.EMail}
</Text>
),
);
};
const resendText = () => {
return reactStringReplace(
lang.account.registration.stepTwo.resend[0],
'${resend}',
(match, i) => (
<Text
key={match + i}
color={'primary.400'}
onPress={() => {
setLoading(true);
resendMail(
store.getState().appVariables.preferences.RegisterProcess.EMail,
toast,
)
.then(() => {
setLoading(false);
})
.catch(() => {
setLoading(false);
});
}}>
{lang.account.registration.stepTwo.resend[1]}
</Text>
),
);
};
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 (
<ScrollView keyboardShouldPersistTaps="handled">
<Box alignItems="center" mt={5}>
<Center w="75%" maxW="1000px">
<Text>{headerText()}</Text>
<Box>
<FormControl isDisabled={isLoading} isInvalid={errors.noEntered}>
<ConfirmationCodeField
cellCount={cellCount}
charType="number"
disabled={isLoading}
rest={{mt: 5}}
onChange={(text: string) => {
setValues({code: text});
setErrors(initNoErrors);
}}
onFinish={(text: string) => validate(text)}
/>
<FormControl.ErrorMessage
leftIcon={<WarningOutlineIcon size="xs" />}>
{lang.account.registration.stepTwo.noCodeEntered}
</FormControl.ErrorMessage>
</FormControl>
</Box>
</Center>
<Container marginY={5}>
<Agreement />
</Container>
<Button
w="75%"
maxW="350px"
colorScheme="primary"
rounded="xl"
_text={{fontSize: 'xl'}}
isLoading={isLoading}
onPress={() => {
if (values.code.length === cellCount) {
validate(values.code);
} else {
let err = {...errors};
err.noEntered = true;
setErrors(err);
}
}}>
{lang.account.registration.stepTwo.button}
</Button>
<Container marginY={5}>
<Text textAlign={'justify'} color={'white.900'}>
{resendText()}
</Text>
</Container>
</Box>
</ScrollView>
);
}
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<RootScreenNavigationProp>();
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<TextInputFocusEventData>) => {
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<TextInputFocusEventData>) => {
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<TextInputFocusEventData>) => {
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<TextInputFocusEventData>) => {
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<React.SetStateAction<inputElementType>>,
valConst: inputElementType,
autofocus?: boolean,
) => {
const isPassword =
val.isPassword === true || val.isPassword === 'passwordRepeat';
return (
<FormControl
w="75%"
maxW="350px"
isRequired
isDisabled={isLoading}
isInvalid={val.errorIndex !== 'none'}>
<FormControl.Label>{val.label}</FormControl.Label>
<Input
autoFocus={autofocus}
type={isPassword && showPassword === false ? 'password' : 'text'}
placeholder={''}
keyboardType={'default'}
onChange={valConst.onTextChange}
InputRightElement={
isPassword ? (
<IconButton
mr="2"
onPress={() => setShowPassword(!showPassword)}
icon={
<MaterialIcons
size={20}
name={showPassword ? 'visibility' : 'visibility-off'}
/>
}
borderRadius="full"
/>
) : undefined
}
/>
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
{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}
</FormControl.ErrorMessage>
</FormControl>
);
};
return (
<ScrollView keyboardShouldPersistTaps="handled">
<VStack space={3} alignItems="center" mt={5}>
<Container maxWidth={'100%'} alignItems={'center'}>
<Center>
<Text>{lang.account.registration.stepFinal.displayName}</Text>
<NameDisplay
UserName={
valuesUserName.input !== ''
? valuesUserName.input
: lang.account.registration.stepFinal.userName
}
AccountName={
valuesAccountName.input !== ''
? valuesAccountName.input
: lang.account.registration.stepFinal.accountName
}
/>
</Center>
</Container>
{inputElement(valuesUserName, setValuesUserName, userName)}
{inputElement(valuesAccountName, setValuesAccountName, accountName)}
{inputElement(valuesPassword, setValuesPassword, password)}
{inputElement(valuesPasswordRe, setValuesPasswordRe, passwordRe)}
</VStack>
<Center>
<Container mt={5}>
<Agreement />
</Container>
<Button
marginY={5}
w="75%"
maxW="350px"
colorScheme="primary"
rounded="xl"
_text={{fontSize: 'xl'}}
isLoading={isLoading}
onPress={() => {}}>
{lang.account.registration.stepFinal.button}
</Button>
</Center>
</ScrollView>
);
}

View File

@ -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) => (
<Alert
maxWidth="95%"
alignSelf="center"
flexDirection="row"
status={status ? status : 'info'}
variant={variant}
{...rest}>
<VStack space={1} flexShrink={1} w="100%">
<HStack
flexShrink={1}
alignItems="center"
justifyContent="space-between">
<HStack space={2} flexShrink={1} alignItems="center">
<Alert.Icon />
<Text
fontSize="md"
fontWeight="medium"
flexShrink={1}
color={
variant === 'solid'
? 'lightText'
: variant !== 'outline'
? 'darkText'
: null
}>
{title}
</Text>
</HStack>
{isClosable ? (
<IconButton
variant="unstyled"
icon={<CloseIcon size="3" />}
_icon={{
color: variant === 'solid' ? 'lightText' : 'darkText',
}}
onPress={() => toast.close(id)}
/>
) : null}
</HStack>
<Text
px="6"
color={
variant === 'solid'
? 'lightText'
: variant !== 'outline'
? 'darkText'
: null
}>
{description}
</Text>
</VStack>
</Alert>
);
toast.show({
render: ({id}: {id: string}) => {
return <ToastAlert id={id} {...item} />;
},
});
}
export default showToast;

View File

@ -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<void> {
}
//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: '',
},
};

View File

@ -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<PREFERENCES_VARS>) => {
state.preferences = action.payload;
},
setRegisterProcess: (state, action: PayloadAction<RegisterProcess>) => {
state.preferences.RegisterProcess = action.payload;
},
},
});

View File

@ -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',
},
};
};

View File

@ -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 (
<AnimationView // Special animatable View
style={{
flex: 1,
opacity: motionProps.opacity, // Bind opacity to animated value
}}>
{props.children}
</AnimationView>
);
};
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 (
<AnimationView // Special animatable View
style={{
flex: 1,
transform: [{translateX: motionProps.translateX}],
}}>
{props.children}
</AnimationView>
);
};
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 (
<AnimationView // Special animatable View
style={{
flex: 1,
transform: [{translateX: motionProps.translateX}],
}}>
{props.children}
</AnimationView>
);
};

168
src/caj/helper/request.ts Normal file
View File

@ -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<T1 extends FetchTypes>(type: T1): Promise<T1> {
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<T1>((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);
});
});
}

44
src/caj/helper/types.ts Normal file
View File

@ -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,}))$/,
);
},
};

View File

@ -0,0 +1,3 @@
import {getUserAgent} from 'react-native-user-agent';
export default getUserAgent;

View File

@ -0,0 +1,2 @@
const getUserAgent = undefined;
export default getUserAgent;

View File

@ -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 {

View File

@ -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',
},
},
},
};

View File

@ -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<AccountStackNavigatorParamList>();
export type AccountScreenNavigationProp =
NativeStackNavigationProp<AccountStackNavigatorParamList>;
function AccountTab() {
const lang = useSelector((state: RootState) => state.appVariables.lang);
const theme = useSelector(
(state: RootState) => state.appVariables.preferences.theme,
);
return (
<AccountStack.Navigator>
<AccountStack.Screen
name="Overview"
options={{
animation: 'slide_from_left',
title: lang.navigation.home.account,
headerShown: true,
...defaultHeaderStyle(theme),
}}
component={AccountScreen}
/>
</AccountStack.Navigator>
);
}
function AccountScreen() {
const theme = useSelector(
(state: RootState) => state.appVariables.preferences.theme,
);
return (
<>
<Box
bg="blackBG.400"
{...(theme === ThemeMode.Darkest
? {borderBottomWidth: 1, borderColor: 'white.900'}
: {})}
style={{
width: '100%',
paddingVertical: 20,
borderBottomLeftRadius: 20,
}}>
<AccountInfoBanner />
</Box>
<NotLoggedIn />
</>
);
}
export default AccountTab;

View File

@ -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<CalendarStackNavigatorParamList>();
export type CalendarScreenNavigationProp =
NativeStackNavigationProp<CalendarStackNavigatorParamList>;
function CalendarTab() {
const lang = useSelector((state: RootState) => state.appVariables.lang);
const theme = useSelector(
(state: RootState) => state.appVariables.preferences.theme,
);
return (
<CalendarStack.Navigator>
<CalendarStack.Screen
name="Overview"
options={{
title: lang.navigation.home.calendar,
headerShown: true,
...defaultHeaderStyle(theme),
}}
component={CalendarScreen}
/>
</CalendarStack.Navigator>
);
}
function CalendarScreen() {
return (
<Center>
<NotLoggedIn />
</Center>
);
}
export default CalendarTab;

View File

@ -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<ChatStackNavigatorParamList>();
export type ChatScreenNavigationProp =
NativeStackNavigationProp<ChatStackNavigatorParamList>;
function ChatTab() {
const lang = useSelector((state: RootState) => state.appVariables.lang);
const theme = useSelector(
(state: RootState) => state.appVariables.preferences.theme,
);
return (
<ChatStack.Navigator>
<ChatStack.Screen
name="Overview"
options={{
title: lang.navigation.home.chat,
headerShown: true,
...defaultHeaderStyle(theme),
}}
component={ChatScreen}
/>
</ChatStack.Navigator>
);
}
function ChatScreen() {
return (
<Center>
<NotLoggedIn />
</Center>
);
}
export default ChatTab;

View File

@ -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<MapsStackNavigatorParamList>();
export type MapsScreenNavigationProp =
NativeStackNavigationProp<MapsStackNavigatorParamList>;
function MapsTab() {
const lang = useSelector((state: RootState) => state.appVariables.lang);
const theme = useSelector(
(state: RootState) => state.appVariables.preferences.theme,
);
return (
<MapsStack.Navigator>
<MapsStack.Screen
name="Overview"
options={{
title: 'Maps',
headerShown: true,
...defaultHeaderStyle(theme),
}}
component={MapsScreen}
/>
</MapsStack.Navigator>
);
}
function MapsScreen() {
return <Center>MapsScreen</Center>;
}
export default MapsTab;

View File

@ -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;

View File

@ -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',
],
},
};