Merge branch 'nikolaik/migrate-ts' into 'main'
Migrate to typescript 💥 See merge request edb/cnapp!3
This commit is contained in:
commit
ce43893f9d
36 files changed
+392
-131
No files matched your search
+21
-4
@@ -3,10 +3,13 @@ module.exports = {
|
||||
extends: [
|
||||
'@react-native',
|
||||
'airbnb-base',
|
||||
'airbnb-typescript/base',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:import/errors',
|
||||
'plugin:import/warnings',
|
||||
'plugin:import/typescript',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
rules: {
|
||||
'class-methods-use-this': 'error', // FIXME: refactor
|
||||
@@ -16,11 +19,11 @@ module.exports = {
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-use-before-define': ['error', { functions: true, classes: true, variables: false }], // disable the rule for variables, but enable it for functions and classes
|
||||
'@typescript-eslint/no-use-before-define': ['error', { functions: true, classes: true, variables: false }], // disable the rule for variables, but enable it for functions and classes
|
||||
|
||||
'react/prefer-stateless-function': 1,
|
||||
'react/prop-types': 'off', // FIXME: typescript
|
||||
'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx'] }],
|
||||
'react/prop-types': 'off',
|
||||
'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
|
||||
'react/sort-comp': 'off',
|
||||
'react-native/no-inline-styles': 'off', // FIXME: refactor
|
||||
'import/no-extraneous-dependencies': [
|
||||
@@ -41,10 +44,24 @@ module.exports = {
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
},
|
||||
plugins: ['prettier', 'unused-imports'],
|
||||
parser: '@babel/eslint-parser',
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
},
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['./**/*.js'],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
default:
|
||||
image: node:20-slim
|
||||
cache:
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
check_types:
|
||||
stage: test
|
||||
script:
|
||||
- npm i
|
||||
- npm run check-types
|
||||
|
||||
lint:
|
||||
stage: test
|
||||
script:
|
||||
- npm i
|
||||
- npm run lint
|
||||
File renamed without changes.
+2
-2
@@ -2,11 +2,11 @@
|
||||
import { AppRegistry } from 'react-native';
|
||||
// import * as Sentry from '@sentry/react-native';
|
||||
|
||||
import { name as appName } from './app.json';
|
||||
import { expo as expoConfig } from './app.json';
|
||||
import App from './src/App';
|
||||
|
||||
// if (Config.SENTRY_DSN) {
|
||||
// Sentry.init({ dsn: Config.SENTRY_DSN });
|
||||
// }
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
AppRegistry.registerComponent(expoConfig.name, () => App);
|
||||
Generated
+166
-16
@@ -27,7 +27,7 @@
|
||||
"moment": "^2.29.4",
|
||||
"native-base": "^3.4.28",
|
||||
"react": "18.2.0",
|
||||
"react-hook-form": "^7.45.2",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-native": "0.74.3",
|
||||
"react-native-confetti": "^0.1.0",
|
||||
"react-native-gesture-handler": "~2.16.1",
|
||||
@@ -48,16 +48,21 @@
|
||||
"@babel/eslint-parser": "^7.24.7",
|
||||
"@react-native/eslint-config": "^0.74.85",
|
||||
"@testing-library/react-native": "^12.3.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/react": "18.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-unused-imports": "^3.2.0",
|
||||
"prettier": "^3.3.2"
|
||||
"prettier": "^3.3.2",
|
||||
"typescript": "^5.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@@ -3835,6 +3840,18 @@
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect-utils": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
|
||||
"integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"jest-get-type": "^29.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/fake-timers": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
|
||||
@@ -7981,6 +7998,48 @@
|
||||
"@types/istanbul-lib-report": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest": {
|
||||
"version": "29.5.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
|
||||
"integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"expect": "^29.0.0",
|
||||
"pretty-format": "^29.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
@@ -8020,12 +8079,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.2.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.18.tgz",
|
||||
"integrity": "sha512-da4NTSeBv/P34xoZPhtcLkmZuJ+oYaCxHmyHzwaDQo9RQPBeXV+06gEk2FpqEcsX9XrnNLvRpVh6bdavDSjtiQ==",
|
||||
"version": "18.2.79",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz",
|
||||
"integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
@@ -8039,11 +8097,6 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||
@@ -10427,6 +10480,19 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.17.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz",
|
||||
"integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -10790,6 +10856,20 @@
|
||||
"eslint-plugin-import": "^2.25.2"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-airbnb-typescript": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-18.0.0.tgz",
|
||||
"integrity": "sha512-oc+Lxzgzsu8FQyFVa4QFaVKiitTYiiW3frB9KYW5OWdPrqFc7FzxgB20hP4cHMlr+MBzGcLl3jnCOVOydL9mIg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eslint-config-airbnb-base": "^15.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"eslint": "^8.56.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
|
||||
@@ -10839,6 +10919,31 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-import-resolver-typescript": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz",
|
||||
"integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"enhanced-resolve": "^5.12.0",
|
||||
"eslint-module-utils": "^2.7.4",
|
||||
"fast-glob": "^3.3.1",
|
||||
"get-tsconfig": "^4.5.0",
|
||||
"is-core-module": "^2.11.0",
|
||||
"is-glob": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "*",
|
||||
"eslint-plugin-import": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-module-utils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz",
|
||||
@@ -11680,6 +11785,22 @@
|
||||
"which": "bin/which"
|
||||
}
|
||||
},
|
||||
"node_modules/expect": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
|
||||
"integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jest/expect-utils": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/expo": {
|
||||
"version": "51.0.18",
|
||||
"resolved": "https://registry.npmjs.org/expo/-/expo-51.0.18.tgz",
|
||||
@@ -12425,6 +12546,18 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz",
|
||||
"integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/getenv": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz",
|
||||
@@ -17228,9 +17361,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.45.2",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.45.2.tgz",
|
||||
"integrity": "sha512-9s45OdTaKN+4NSTbXVqeDITd/nwIg++nxJGL8+OD5uf1DxvhsXQ641kaYHk5K28cpIOTYm71O/fYk7rFaygb3A==",
|
||||
"version": "7.52.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.1.tgz",
|
||||
"integrity": "sha512-uNKIhaoICJ5KQALYZ4TOaOLElyM+xipord+Ha3crEFhTntdLvWZqVY49Wqd/0GiVCA/f9NjemLeiNPjG7Hpurg==",
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
@@ -17239,7 +17372,7 @@
|
||||
"url": "https://opencollective.com/react-hook-form"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17 || ^18"
|
||||
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
@@ -17977,6 +18110,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve.exports": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
|
||||
@@ -18956,6 +19098,15 @@
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||
@@ -19470,7 +19621,6 @@
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
|
||||
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
+9
-3
@@ -8,7 +8,8 @@
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"lint": "eslint .",
|
||||
"format": "eslint --fix ."
|
||||
"format": "eslint --fix .",
|
||||
"check-types": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.17",
|
||||
@@ -30,7 +31,7 @@
|
||||
"moment": "^2.29.4",
|
||||
"native-base": "^3.4.28",
|
||||
"react": "18.2.0",
|
||||
"react-hook-form": "^7.45.2",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-native": "0.74.3",
|
||||
"react-native-confetti": "^0.1.0",
|
||||
"react-native-gesture-handler": "~2.16.1",
|
||||
@@ -51,16 +52,21 @@
|
||||
"@babel/eslint-parser": "^7.24.7",
|
||||
"@react-native/eslint-config": "^0.74.85",
|
||||
"@testing-library/react-native": "^12.3.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/react": "18.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-unused-imports": "^3.2.0",
|
||||
"prettier": "^3.3.2"
|
||||
"prettier": "^3.3.2",
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export default function App() {
|
||||
{/* FIXME: Read urlScheme and merchantIdentifier from .Config */}
|
||||
<StripeProvider
|
||||
publishableKey={process.env.EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY || ''}
|
||||
urlScheme={Constants.expoConfig.scheme}
|
||||
urlScheme={Constants.expoConfig.scheme as string}
|
||||
merchantIdentifier="merchant.no.neuf.chateau" // https://developer.apple.com/account/resources/identifiers/list/merchant
|
||||
>
|
||||
<Dusken />
|
||||
File renamed without changes.
@@ -11,6 +11,7 @@ import Login from './modules/login/Login';
|
||||
import Membership from './modules/membership/Membership';
|
||||
import UserRegister from './modules/register/UserRegister';
|
||||
import theme from './theme';
|
||||
import type { EventStackParamList, HomeTabParamList, UserStackParamList } from './types/navigation';
|
||||
|
||||
// This looks ugly, even though it's a recommended style from the branding profile.
|
||||
// TODO: Change this
|
||||
@@ -21,9 +22,9 @@ const defaultNavigationStyle = {
|
||||
headerStyle: {
|
||||
backgroundColor: theme.colors.neufPink,
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
const EventStack = createNativeStackNavigator();
|
||||
const EventStack = createNativeStackNavigator<EventStackParamList>();
|
||||
const EventStackScreen = () => (
|
||||
<EventStack.Navigator initialRouteName="EventList" screenOptions={{ ...defaultNavigationStyle }}>
|
||||
<EventStack.Screen
|
||||
@@ -41,7 +42,7 @@ const EventStackScreen = () => (
|
||||
</EventStack.Navigator>
|
||||
);
|
||||
|
||||
const UserStack = createNativeStackNavigator();
|
||||
const UserStack = createNativeStackNavigator<UserStackParamList>();
|
||||
const UserStackScreen = () => (
|
||||
<UserStack.Navigator initialRouteName="Membership" screenOptions={{ ...defaultNavigationStyle }}>
|
||||
<UserStack.Screen name="Membership" component={Membership} options={{ title: 'Medlemskap' }} />
|
||||
@@ -56,7 +57,7 @@ const UserStackScreen = () => (
|
||||
</UserStack.Navigator>
|
||||
);
|
||||
|
||||
const HomeTabs = createBottomTabNavigator();
|
||||
const HomeTabs = createBottomTabNavigator<HomeTabParamList>();
|
||||
const EventIcon = ({ color, size }) => <Icon name="event" size={size} color={color} style={{ marginTop: 5 }} />;
|
||||
const UserIcon = ({ color, size }) => <Icon name="user" size={size} color={color} style={{ marginTop: 5 }} />;
|
||||
const AboutIcon = ({ color, size }) => <Icon name="info" size={size} color={color} style={{ marginTop: 5 }} />;
|
||||
@@ -2,7 +2,7 @@ import 'react-native';
|
||||
import { render } from '@testing-library/react-native';
|
||||
import React from 'react';
|
||||
|
||||
import App from '../App';
|
||||
import App from 'src/App';
|
||||
|
||||
it('renders correctly', () => {
|
||||
const { toJSON } = render(<App />);
|
||||
File renamed without changes.
@@ -12,6 +12,7 @@ import {
|
||||
membershipChargeRequest,
|
||||
membershipChargeSuccess,
|
||||
} from './actions';
|
||||
import type { UserRegisterFormValues } from './modules/register/useUserRegister';
|
||||
import { fetchWithTimeout } from './utils';
|
||||
|
||||
export function requestLogin(username, password) {
|
||||
@@ -80,7 +81,7 @@ export function requestUserData(authToken) {
|
||||
};
|
||||
}
|
||||
|
||||
export function requestRegisterUser({ firstName, lastName, email, phoneNumber, password }) {
|
||||
export function requestRegisterUser({ firstName, lastName, email, phoneNumber, password }: UserRegisterFormValues) {
|
||||
return (dispatch) => {
|
||||
dispatch(registerUserRequest());
|
||||
|
||||
File renamed without changes.
@@ -1,10 +1,16 @@
|
||||
import { Input, Text, WarningOutlineIcon, FormControl } from 'native-base';
|
||||
import { Input, Text, WarningOutlineIcon, FormControl, IInputProps } from 'native-base';
|
||||
import React from 'react';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Controller, UseControllerProps, useForm } from 'react-hook-form';
|
||||
import { ReturnKeyTypeOptions } from 'react-native';
|
||||
|
||||
type FormInputProps = {
|
||||
name: string;
|
||||
label: string;
|
||||
rules?: UseControllerProps['rules'];
|
||||
returnKeyType?: ReturnKeyTypeOptions;
|
||||
} & IInputProps;
|
||||
|
||||
const FormInput = ({
|
||||
control,
|
||||
errors,
|
||||
name,
|
||||
label,
|
||||
rules = {
|
||||
@@ -12,13 +18,18 @@ const FormInput = ({
|
||||
},
|
||||
returnKeyType = 'next',
|
||||
...rest
|
||||
}) => {
|
||||
}: FormInputProps) => {
|
||||
const {
|
||||
formState: { errors },
|
||||
control,
|
||||
} = useForm();
|
||||
const fieldError = errors[name];
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
rules={rules}
|
||||
render={({ field: { onChange, onBlur, value, ref } }) => (
|
||||
<FormControl isInvalid={Boolean(errors[name])}>
|
||||
<FormControl isInvalid={Boolean(fieldError)}>
|
||||
<FormControl.Label>{label}</FormControl.Label>
|
||||
<Input
|
||||
ref={ref}
|
||||
@@ -28,9 +39,9 @@ const FormInput = ({
|
||||
value={value}
|
||||
{...rest}
|
||||
/>
|
||||
{errors[name] && (
|
||||
{fieldError && (
|
||||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
<Text>{errors[name].message}</Text>
|
||||
<Text>{fieldError.message.toString()}</Text>
|
||||
</FormControl.ErrorMessage>
|
||||
)}
|
||||
</FormControl>
|
||||
@@ -1,6 +1,10 @@
|
||||
import { useStripe } from '@stripe/stripe-react-native';
|
||||
import { useCallback, useRef, useEffect } from 'react';
|
||||
import { Linking } from 'react-native';
|
||||
import type { TypedUseSelectorHook } from 'react-redux';
|
||||
import { useDispatch, useSelector, useStore } from 'react-redux';
|
||||
|
||||
import type { AppDispatch, AppStore, RootState } from './reduxStore';
|
||||
|
||||
export function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
@@ -46,3 +50,8 @@ export const useDeepLinking = () => {
|
||||
return () => deepLinkListener.remove();
|
||||
}, [handleDeepLink]);
|
||||
};
|
||||
|
||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch;
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
export const useAppStore: () => AppStore = useStore;
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
declare module '*.png';
|
||||
@@ -13,8 +13,9 @@ import {
|
||||
} from 'react-native';
|
||||
import { Button } from 'react-native-ui-lib';
|
||||
|
||||
import theme from 'src/theme';
|
||||
|
||||
import logo from '../../../assets/DNS-logo-svart.png';
|
||||
import theme from '../../theme';
|
||||
|
||||
const CHATEAU_NEUF_ADDRESS = 'Det Norske Studentersamfund, Slemdalsveien 15, 0369 Oslo';
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
} from 'react-native';
|
||||
import RenderHtml from 'react-native-render-html';
|
||||
|
||||
import theme from '../../theme';
|
||||
import theme from 'src/theme';
|
||||
import type { EventDetailProps } from 'src/types/navigation';
|
||||
|
||||
moment.locale('nb');
|
||||
|
||||
@@ -157,7 +158,7 @@ const EventDetail = ({
|
||||
route: {
|
||||
params: { event },
|
||||
},
|
||||
}) => {
|
||||
}: EventDetailProps) => {
|
||||
const { width } = useWindowDimensions();
|
||||
return (
|
||||
<ScrollView>
|
||||
@@ -5,12 +5,13 @@ import { StyleSheet, View, Platform, SectionList, TouchableOpacity } from 'react
|
||||
import { Button } from 'react-native-ui-lib';
|
||||
import 'moment/locale/nb';
|
||||
|
||||
import theme from 'src/theme';
|
||||
|
||||
import useEventList from './useEventList';
|
||||
import theme from '../../theme';
|
||||
|
||||
moment.locale('nb');
|
||||
|
||||
const _formatTime = (time) => {
|
||||
const formatTime = (time: string) => {
|
||||
return moment(time).format('llll');
|
||||
};
|
||||
|
||||
@@ -36,7 +37,7 @@ const EventList = () => {
|
||||
<Text style={styles.listItemTitle} numberOfLines={1}>
|
||||
{item.title.decoded}
|
||||
</Text>
|
||||
<Text style={styles.listItemTime}>{_formatTime(item.start_time)}</Text>
|
||||
<Text style={styles.listItemTime}>{formatTime(item.start_time)}</Text>
|
||||
</View>
|
||||
<View style={styles.arrow}>
|
||||
<ArrowForwardIcon size={4} color={theme.colors.neufBlack} marginTop={3} />
|
||||
@@ -80,7 +81,7 @@ const EventList = () => {
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
onEndReached={handleLoadMore}
|
||||
onEndReachedThreshould={10}
|
||||
onEndReachedThreshold={10}
|
||||
initialNumToRender={10}
|
||||
ListFooterComponent={Footer}
|
||||
style={[styles.card, styles.list]}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import moment from 'moment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import 'moment/locale/nb';
|
||||
|
||||
import { fetchWithTimeout } from '../../utils';
|
||||
import 'moment/locale/nb';
|
||||
import type { EventType } from 'src/types/data';
|
||||
import { fetchWithTimeout } from 'src/utils';
|
||||
|
||||
moment.locale('nb');
|
||||
|
||||
@@ -81,8 +82,8 @@ const useEventList = () => {
|
||||
}, []);
|
||||
|
||||
return {
|
||||
showEvent: (event) => {
|
||||
navigate('EventDetail', { event });
|
||||
showEvent: (event: EventType) => {
|
||||
navigate('EventStack', { screen: 'EventDetail', params: { event } });
|
||||
},
|
||||
handleRefresh,
|
||||
handleLoadMore,
|
||||
@@ -3,13 +3,14 @@ import React from 'react';
|
||||
import { ActivityIndicator, Linking, StyleSheet, View, Text } from 'react-native';
|
||||
import { Button } from 'react-native-ui-lib';
|
||||
|
||||
import FormErrors from 'src/components/FormErrors';
|
||||
import FormInput from 'src/components/FormInput';
|
||||
import theme from 'src/theme';
|
||||
|
||||
import useLogin from './useLogin';
|
||||
import FormErrors from '../../components/FormErrors';
|
||||
import FormInput from '../../components/FormInput';
|
||||
import theme from '../../theme';
|
||||
|
||||
const Login = () => {
|
||||
const { isLoggingIn, errors, control, setFocus, onSubmit } = useLogin();
|
||||
const { isLoggingIn, errors, setFocus, onSubmit } = useLogin();
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
@@ -19,8 +20,6 @@ const Login = () => {
|
||||
name="email"
|
||||
label="E-post / Brukernavn"
|
||||
keyboardType="email-address"
|
||||
control={control}
|
||||
errors={errors}
|
||||
autoFocus
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
@@ -30,8 +29,6 @@ const Login = () => {
|
||||
name="password"
|
||||
label="Passord"
|
||||
secureTextEntry
|
||||
control={control}
|
||||
errors={errors}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onSubmitEditing={onSubmit}
|
||||
@@ -1,25 +1,29 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { requestLogin } from '../../api';
|
||||
import { requestLogin } from 'src/api';
|
||||
import { useAppDispatch, useAppSelector } from 'src/hooks';
|
||||
|
||||
interface LoginFormValues {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const useLogin = () => {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const isAuthenticated = useSelector((store) => store.isAuthenticated);
|
||||
const isLoggingIn = useSelector((store) => store.isLoggingIn);
|
||||
const loginError = useSelector((store) => store.loginError);
|
||||
const isAuthenticated = useAppSelector((store) => store.isAuthenticated);
|
||||
const isLoggingIn = useAppSelector((store) => store.isLoggingIn);
|
||||
const loginError = useAppSelector((store) => store.loginError);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setFocus,
|
||||
} = useForm();
|
||||
} = useForm<LoginFormValues>();
|
||||
|
||||
const onSubmit = handleSubmit(({ email, password }) => {
|
||||
dispatch(requestLogin(email, password));
|
||||
@@ -32,7 +36,7 @@ const useLogin = () => {
|
||||
}
|
||||
}, [isAuthenticated, navigation]);
|
||||
|
||||
return { onSubmit, isLoggingIn, control, setFocus, errors: { ...errors, ...loginError } };
|
||||
return { onSubmit, isLoggingIn, setFocus, errors: { ...errors, ...loginError } };
|
||||
};
|
||||
|
||||
export default useLogin;
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, ActivityIndicator } from 'react-native';
|
||||
|
||||
import theme from 'src/theme';
|
||||
|
||||
import MembershipInfo from './MembershipInfo';
|
||||
import Proof from './Proof';
|
||||
import useMembership from './useMembership';
|
||||
import theme from '../../theme';
|
||||
|
||||
const Membership = () => {
|
||||
const { isFetchingUserData, onRegisterPress, onLoginPress, onLogoutPress, user, isAuthenticated, fetchUser } =
|
||||
+1
-1
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { ScrollView, Text, View, StyleSheet } from 'react-native';
|
||||
import { Button } from 'react-native-ui-lib';
|
||||
|
||||
import theme from '../../theme';
|
||||
import theme from 'src/theme';
|
||||
|
||||
const MembershipInfo = ({ onRegister, onLogin }) => (
|
||||
<ScrollView>
|
||||
@@ -13,9 +13,10 @@ import {
|
||||
import Confetti from 'react-native-confetti';
|
||||
import { Button, Incubator } from 'react-native-ui-lib';
|
||||
|
||||
import theme from 'src/theme';
|
||||
|
||||
import useProof from './useProof';
|
||||
import logo from '../../../assets/DNS-logo-svart.png';
|
||||
import theme from '../../theme';
|
||||
|
||||
const { Toast } = Incubator;
|
||||
|
||||
+12
-13
@@ -1,28 +1,27 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { logout } from '../../actions';
|
||||
import { requestUserData } from '../../api';
|
||||
import { usePrevious } from '../../hooks';
|
||||
import { logout } from 'src/actions';
|
||||
import { requestUserData } from 'src/api';
|
||||
import { useAppDispatch, useAppSelector, usePrevious } from 'src/hooks';
|
||||
|
||||
const useMembership = () => {
|
||||
const navigation = useNavigation();
|
||||
const dispatch = useDispatch();
|
||||
const isAuthenticated = useSelector((store) => store.isAuthenticated);
|
||||
const isFetchingUserData = useSelector((store) => store.isFetchingUserData);
|
||||
const lastOrder = useSelector((store) => store.lastOrder);
|
||||
const user = useSelector((store) => store.user);
|
||||
const userToken = useSelector((store) => store.userToken);
|
||||
const dispatch = useAppDispatch();
|
||||
const isAuthenticated = useAppSelector((store) => store.isAuthenticated);
|
||||
const isFetchingUserData = useAppSelector((store) => store.isFetchingUserData);
|
||||
const lastOrder = useAppSelector((store) => store.lastOrder);
|
||||
const user = useAppSelector((store) => store.user);
|
||||
const userToken = useAppSelector((store) => store.userToken);
|
||||
const prevUserToken = usePrevious(userToken);
|
||||
const prevLastOrder = usePrevious(lastOrder);
|
||||
|
||||
const onLogoutPress = () => {
|
||||
dispatch(logout());
|
||||
navigation.navigate('Membership');
|
||||
navigation.navigate('UserStack', { screen: 'Membership' });
|
||||
};
|
||||
const onRegisterPress = () => navigation.navigate('UserRegister');
|
||||
const onLoginPress = () => navigation.navigate('Login');
|
||||
const onRegisterPress = () => navigation.navigate('UserStack', { screen: 'UserRegister' });
|
||||
const onLoginPress = () => navigation.navigate('UserStack', { screen: 'Login' });
|
||||
const fetchUser = useCallback(() => {
|
||||
dispatch(requestUserData(userToken));
|
||||
}, [dispatch, userToken]);
|
||||
@@ -4,7 +4,8 @@ import Constants from 'expo-constants';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { requestStripePaymentSheet } from '../../api';
|
||||
import { requestStripePaymentSheet } from 'src/api';
|
||||
import { RootState } from 'src/reduxStore';
|
||||
|
||||
const PROOF_QUERY = gql`
|
||||
{
|
||||
@@ -24,8 +25,8 @@ function getPrice(membershipTypes) {
|
||||
}
|
||||
|
||||
const useProof = ({ user, fetchUser }) => {
|
||||
const userToken = useSelector((state) => state.userToken);
|
||||
const chargeError = useSelector((state) => state.chargeError);
|
||||
const userToken = useSelector((state: RootState) => state.userToken);
|
||||
const chargeError = useSelector((state: RootState) => state.chargeError);
|
||||
const { data, loading } = useQuery(PROOF_QUERY);
|
||||
const { initPaymentSheet, presentPaymentSheet } = useStripe();
|
||||
const [paymentLoading, setPaymentLoading] = useState(false);
|
||||
@@ -3,16 +3,16 @@ import React from 'react';
|
||||
import { ActivityIndicator, StyleSheet, View, Text, ScrollView } from 'react-native';
|
||||
import { Button, Incubator } from 'react-native-ui-lib';
|
||||
|
||||
import FormErrors from 'src/components/FormErrors';
|
||||
import FormInput from 'src/components/FormInput';
|
||||
import theme from 'src/theme';
|
||||
|
||||
import useUserRegister from './useUserRegister';
|
||||
import FormErrors from '../../components/FormErrors';
|
||||
import FormInput from '../../components/FormInput';
|
||||
import theme from '../../theme';
|
||||
|
||||
const { Toast } = Incubator;
|
||||
|
||||
const UserRegister = () => {
|
||||
const { isRegisteringUser, onSubmit, control, isValid, errors, registerError, setFocus, toast, setToast } =
|
||||
useUserRegister();
|
||||
const { isRegisteringUser, onSubmit, isValid, errors, registerError, setFocus, toast, setToast } = useUserRegister();
|
||||
const isDisabled = isRegisteringUser || !isValid;
|
||||
// TODO: validate on type
|
||||
// TODO: highlight errorfields
|
||||
@@ -21,26 +21,11 @@ const UserRegister = () => {
|
||||
<>
|
||||
<View style={styles.card}>
|
||||
<ScrollView>
|
||||
<FormInput
|
||||
name="firstName"
|
||||
label="Fornavn"
|
||||
control={control}
|
||||
errors={errors}
|
||||
autoFocus={true}
|
||||
onSubmitEditing={() => setFocus('lastName')}
|
||||
/>
|
||||
<FormInput
|
||||
name="lastName"
|
||||
label="Etternavn"
|
||||
control={control}
|
||||
errors={errors}
|
||||
onSubmitEditing={() => setFocus('email')}
|
||||
/>
|
||||
<FormInput name="firstName" label="Fornavn" autoFocus onSubmitEditing={() => setFocus('lastName')} />
|
||||
<FormInput name="lastName" label="Etternavn" onSubmitEditing={() => setFocus('email')} />
|
||||
<FormInput
|
||||
name="email"
|
||||
label="E-post"
|
||||
control={control}
|
||||
errors={errors}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
@@ -53,17 +38,13 @@ const UserRegister = () => {
|
||||
<FormInput
|
||||
name="phoneNumber"
|
||||
label="Mobilnummer"
|
||||
control={control}
|
||||
errors={errors}
|
||||
keyboardType="phone-pad"
|
||||
onSubmitEditing={() => setFocus('password')}
|
||||
/>
|
||||
<FormInput
|
||||
name="password"
|
||||
label="Passord"
|
||||
control={control}
|
||||
errors={errors}
|
||||
secureTextEntry={true}
|
||||
secureTextEntry
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="send"
|
||||
+17
-11
@@ -1,28 +1,34 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import { requestRegisterUser } from '../../api';
|
||||
import { usePrevious } from '../../hooks';
|
||||
import { requestRegisterUser } from 'src/api';
|
||||
import { useAppDispatch, useAppSelector, usePrevious } from 'src/hooks';
|
||||
|
||||
export interface UserRegisterFormValues {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const useUserRegister = () => {
|
||||
const dispatch = useDispatch();
|
||||
const dispatch = useAppDispatch();
|
||||
const navigation = useNavigation();
|
||||
const isAuthenticated = useSelector((store) => store.isAuthenticated);
|
||||
const isRegisteringUser = useSelector((store) => store.isRegisteringUser);
|
||||
const registerError = useSelector((store) => store.registerError) || {};
|
||||
const isAuthenticated = useAppSelector((store) => store.isAuthenticated);
|
||||
const isRegisteringUser = useAppSelector((store) => store.isRegisteringUser);
|
||||
const registerError = useAppSelector((store) => store.registerError) || {};
|
||||
const [toast, setToast] = useState(null);
|
||||
|
||||
const isAuthenticatedPrev = usePrevious(isAuthenticated);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors, isValid },
|
||||
setFocus,
|
||||
setError,
|
||||
} = useForm();
|
||||
} = useForm<UserRegisterFormValues>();
|
||||
const onSubmit = handleSubmit((values) => {
|
||||
if (!isValid) {
|
||||
setToast({
|
||||
@@ -38,7 +44,7 @@ const useUserRegister = () => {
|
||||
|
||||
// Display error messages from API on form fields
|
||||
Object.keys(registerError).forEach((key) => {
|
||||
setError(key, { type: 'manual', message: registerError[key] });
|
||||
setError(key as keyof UserRegisterFormValues, { type: 'manual', message: registerError[key] });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,6 +54,6 @@ const useUserRegister = () => {
|
||||
}
|
||||
}, [isAuthenticatedPrev, isAuthenticated, navigation]);
|
||||
|
||||
return { isRegisteringUser, onSubmit, control, isValid, registerError, errors, setFocus, toast, setToast };
|
||||
return { isRegisteringUser, onSubmit, isValid, registerError, errors, setFocus, toast, setToast };
|
||||
};
|
||||
export default useUserRegister;
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from './actions';
|
||||
import { snakeToCamelCase } from './utils';
|
||||
|
||||
// FIXME: type this
|
||||
const initialState = {
|
||||
isAuthenticated: false,
|
||||
isLoggingIn: false,
|
||||
@@ -31,14 +32,14 @@ const initialState = {
|
||||
};
|
||||
|
||||
function formatErrors(errs) {
|
||||
const _errs = {};
|
||||
const errors = {};
|
||||
Object.entries(errs).forEach(([key, value]) => {
|
||||
_errs[snakeToCamelCase(key)] = Array.isArray(value) ? value.join('\n') : value;
|
||||
errors[snakeToCamelCase(key)] = Array.isArray(value) ? value.join('\n') : value;
|
||||
});
|
||||
return _errs;
|
||||
return errors;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line default-param-last
|
||||
// eslint-disable-next-line @typescript-eslint/default-param-last
|
||||
export default function duskenApp(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
/* Login */
|
||||
@@ -15,3 +15,7 @@ const persistedReducer = persistReducer(persistConfig, rootReducer);
|
||||
|
||||
export const store = createStore(persistedReducer, applyMiddleware(...[thunk]));
|
||||
export const persistor = persistStore(store);
|
||||
|
||||
export type AppStore = typeof store;
|
||||
export type RootState = ReturnType<AppStore['getState']>;
|
||||
export type AppDispatch = AppStore['dispatch'];
|
||||
File renamed without changes.
@@ -0,0 +1,13 @@
|
||||
export interface EventType {
|
||||
start_time: string;
|
||||
content: {
|
||||
rendered: string;
|
||||
};
|
||||
venue: string;
|
||||
title: {
|
||||
decoded: string;
|
||||
};
|
||||
thumbnail: {
|
||||
medium_large: string;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { NavigatorScreenParams } from '@react-navigation/native';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
|
||||
import { EventType } from './data';
|
||||
|
||||
export type EventStackParamList = {
|
||||
EventList: undefined;
|
||||
EventDetail: { event: EventType };
|
||||
};
|
||||
|
||||
export type EventDetailProps = NativeStackScreenProps<EventStackParamList, 'EventDetail'>;
|
||||
|
||||
export type UserStackParamList = {
|
||||
Membership: undefined;
|
||||
Login: undefined;
|
||||
UserRegister: undefined;
|
||||
};
|
||||
|
||||
export type HomeTabParamList = {
|
||||
About: undefined;
|
||||
EventStack: NavigatorScreenParams<EventStackParamList>;
|
||||
UserStack: NavigatorScreenParams<UserStackParamList>;
|
||||
};
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace ReactNavigation {
|
||||
interface RootParamList extends HomeTabParamList {}
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,12 @@ export function snakeToCamelCase(val) {
|
||||
return val.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
|
||||
}
|
||||
|
||||
export function fetchWithTimeout(url, options, timeout = 10000) {
|
||||
export function fetchWithTimeout(url: string, options?: RequestInit, timeout = 10000) {
|
||||
/** Wraps fetch to allow it to time out */
|
||||
// FIXME: show timeout message in UI
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) => {
|
||||
new Promise<Response>((_, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error('Fetch request timed out'));
|
||||
}, timeout);
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./"
|
||||
},
|
||||
"extends": "expo/tsconfig.base"
|
||||
}
|
||||
Reference in new issue
Block a user